feat: Import and export manager settings (#2268)

This commit is contained in:
aAbed 2024-11-06 00:28:35 +05:45 committed by GitHub
parent 8c8df698d4
commit a45d9598cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 108 additions and 1 deletions

View File

@ -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",

View File

@ -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.

View File

@ -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) {

View File

@ -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);

View File

@ -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(),
], ],
); );
} }