diff --git a/assets/i18n/strings.i18n.json b/assets/i18n/strings.i18n.json index a6e618ea5c..e491e3e899 100755 --- a/assets/i18n/strings.i18n.json +++ b/assets/i18n/strings.i18n.json @@ -197,6 +197,12 @@ "deleteTempDirLabel": "Delete temporary files", "deleteTempDirHint": "Delete unused temporary files", "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", "exportPatchesHint": "Export patch selection to a JSON file", "exportedPatches": "Patch selection exported", diff --git a/docs/2_4_settings.md b/docs/2_4_settings.md index 08dbf9d2e1..44bfa6951f 100644 --- a/docs/2_4_settings.md +++ b/docs/2_4_settings.md @@ -50,6 +50,7 @@ Learn how to configure ReVanced Manager. - 🔑 Keystore used to sign patched apps - 📄 Remembered selection of patches for each app - ⚙ī¸ Remembered patch options + - 🛠ī¸ Remembered settings > ℹī¸ Note > These can be used to backup and restore or reset settings to default in case of issues. diff --git a/lib/services/manager_api.dart b/lib/services/manager_api.dart index a0a6a92373..28e2191bab 100644 --- a/lib/services/manager_api.dart +++ b/lib/services/manager_api.dart @@ -755,6 +755,36 @@ class ManagerAPI { return jsonDecode(string); } + String exportSettings() { + final Map settings = _prefs + .getKeys() + .fold>({}, (Map map, String key) { + map[key] = _prefs.get(key); + return map; + }); + return jsonEncode(settings); + } + + Future importSettings(String settings) async { + final Map 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) { + _prefs.setStringList( + key, + value.map((a) => json.encode(a.toJson())).toList(), + ); + } + }); + } + void resetAllOptions() { _prefs.getKeys().where((key) => key.startsWith('patchOption-')).forEach( (key) { diff --git a/lib/ui/views/settings/settings_viewmodel.dart b/lib/ui/views/settings/settings_viewmodel.dart index 7ebd0d0d52..542eef42c7 100644 --- a/lib/ui/views/settings/settings_viewmodel.dart +++ b/lib/ui/views/settings/settings_viewmodel.dart @@ -222,6 +222,53 @@ class SettingsViewModel extends BaseViewModel { notifyListeners(); } + Future 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 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 exportPatches() async { try { final File outFile = File(_managerAPI.storedPatchesFile); diff --git a/lib/ui/widgets/settingsView/settings_export_section.dart b/lib/ui/widgets/settingsView/settings_export_section.dart index 3ac636bc62..aff0323f22 100644 --- a/lib/ui/widgets/settingsView/settings_export_section.dart +++ b/lib/ui/widgets/settingsView/settings_export_section.dart @@ -14,6 +14,30 @@ class SExportSection extends StatelessWidget { return SettingsSection( title: t.settingsView.exportSectionTitle, children: [ + 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( contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), title: Text( @@ -114,7 +138,6 @@ class SExportSection extends StatelessWidget { subtitle: Text(t.settingsView.regenerateKeystoreHint), onTap: () => _showDeleteKeystoreDialog(context), ), - // SManageKeystorePasswordUI(), ], ); }