From 10f1c3abe5bea3e295226a913df5eef20259897c Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Fri, 26 Aug 2022 19:48:42 -0400 Subject: [PATCH] Added import/export --- lib/pages/settings.dart | 122 +++++++++++++++++++++++++++++++- lib/services/apps_provider.dart | 36 +++++++++- 2 files changed, 154 insertions(+), 4 deletions(-) diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 237dcd1..57ec8e5 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -1,4 +1,7 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:obtainium/services/apps_provider.dart'; import 'package:obtainium/services/settings_provider.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -13,6 +16,7 @@ class SettingsPage extends StatefulWidget { class _SettingsPageState extends State { @override Widget build(BuildContext context) { + AppsProvider appsProvider = context.read(); SettingsProvider settingsProvider = context.watch(); if (settingsProvider.prefs == null) { settingsProvider.initializeSettings(); @@ -104,17 +108,129 @@ class _SettingsPageState extends State { settingsProvider.updateInterval = value; } }), + const SizedBox( + height: 32, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ElevatedButton( + onPressed: appsProvider.apps.isEmpty + ? null + : () { + appsProvider.exportApps().then((String path) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Exported to $path')), + ); + }); + }, + child: const Text('Export Apps')), + ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext ctx) { + final formKey = GlobalKey(); + final jsonInputController = + TextEditingController(); + + return AlertDialog( + scrollable: true, + title: const Text('Import Apps'), + content: Column(children: [ + const Text( + 'Copy the contents of the Obtainium export file and paste them into the field below:'), + Form( + key: formKey, + child: TextFormField( + minLines: 7, + maxLines: 7, + decoration: const InputDecoration( + helperText: + 'Obtainium export data'), + controller: jsonInputController, + validator: (value) { + if (value == null || + value.isEmpty) { + return 'Please enter your Obtainium export data'; + } + bool isJSON = true; + try { + jsonDecode(value); + } catch (e) { + isJSON = false; + } + if (!isJSON) { + return 'Invalid input'; + } + return null; + }, + ), + ) + ]), + actions: [ + TextButton( + onPressed: () { + if (formKey.currentState! + .validate()) { + appsProvider + .importApps( + jsonInputController + .value.text) + .then((value) { + ScaffoldMessenger.of(context) + .showSnackBar( + SnackBar( + content: Text( + '$value Apps Imported')), + ); + }).catchError((e) { + ScaffoldMessenger.of(context) + .showSnackBar( + SnackBar( + content: + Text(e.toString())), + ); + }).whenComplete(() { + Navigator.of(context).pop(); + }); + } + }, + child: const Text('Import')), + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Cancel')) + ], + ); + }); + }, + child: const Text('Import Apps')) + ], + ), const Spacer(), Row( - mainAxisAlignment: MainAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.center, children: [ - ElevatedButton.icon( + TextButton.icon( + style: ButtonStyle( + foregroundColor: + MaterialStateProperty.resolveWith( + (Set states) { + return Colors.grey; + }), + ), onPressed: () { launchUrlString(settingsProvider.sourceUrl, mode: LaunchMode.externalApplication); }, icon: const Icon(Icons.code), - label: const Text('Source'), + label: Text( + 'Source', + style: Theme.of(context).textTheme.caption, + ), ) ], ), diff --git a/lib/services/apps_provider.dart b/lib/services/apps_provider.dart index 9850e5a..6380291 100644 --- a/lib/services/apps_provider.dart +++ b/lib/services/apps_provider.dart @@ -132,7 +132,7 @@ class AppsProvider with ChangeNotifier { await InstallPlugin.installApk(downloadFile.path, 'dev.imranr.obtainium'); apps[appId]!.app.installedVersion = apps[appId]!.app.latestVersion; - saveApp(apps[appId]!.app); + await saveApp(apps[appId]!.app); } Future getAppsDir() async { @@ -236,6 +236,40 @@ class AppsProvider with ChangeNotifier { return updateAppIds; } + Future exportApps() async { + Directory? exportDir = Directory('/storage/emulated/0/Download'); + String path = 'Downloads'; + if (!exportDir.existsSync()) { + exportDir = await getExternalStorageDirectory(); + path = exportDir!.path; + } + File export = File( + '${exportDir!.path}/obtainium-export-${DateTime.now().millisecondsSinceEpoch}.json'); + export.writeAsStringSync( + jsonEncode(apps.values.map((e) => e.app.toJson()).toList())); + return path; + } + + Future importApps(String appsJSON) async { + // FilePickerResult? result = await FilePicker.platform.pickFiles(); + + // if (result != null) { + // String appsJSON = File(result.files.single.path!).readAsStringSync(); + List importedApps = (jsonDecode(appsJSON) as List) + .map((e) => App.fromJson(e)) + .toList(); + for (App a in importedApps) { + a.installedVersion = + apps.containsKey(a.id) ? apps[a]?.app.installedVersion : null; + await saveApp(a); + } + notifyListeners(); + return importedApps.length; + // } else { + // User canceled the picker + // } + } + @override void dispose() { IsolateNameServer.removePortNameMapping('downloader_send_port');