mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-11-07 16:43:29 +01:00
Added import/export
This commit is contained in:
@@ -1,4 +1,7 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:obtainium/services/apps_provider.dart';
|
||||||
import 'package:obtainium/services/settings_provider.dart';
|
import 'package:obtainium/services/settings_provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
@@ -13,6 +16,7 @@ class SettingsPage extends StatefulWidget {
|
|||||||
class _SettingsPageState extends State<SettingsPage> {
|
class _SettingsPageState extends State<SettingsPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
AppsProvider appsProvider = context.read<AppsProvider>();
|
||||||
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
||||||
if (settingsProvider.prefs == null) {
|
if (settingsProvider.prefs == null) {
|
||||||
settingsProvider.initializeSettings();
|
settingsProvider.initializeSettings();
|
||||||
@@ -104,17 +108,129 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
settingsProvider.updateInterval = value;
|
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<FormState>();
|
||||||
|
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(),
|
const Spacer(),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
ElevatedButton.icon(
|
TextButton.icon(
|
||||||
|
style: ButtonStyle(
|
||||||
|
foregroundColor:
|
||||||
|
MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) {
|
||||||
|
return Colors.grey;
|
||||||
|
}),
|
||||||
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
launchUrlString(settingsProvider.sourceUrl,
|
launchUrlString(settingsProvider.sourceUrl,
|
||||||
mode: LaunchMode.externalApplication);
|
mode: LaunchMode.externalApplication);
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.code),
|
icon: const Icon(Icons.code),
|
||||||
label: const Text('Source'),
|
label: Text(
|
||||||
|
'Source',
|
||||||
|
style: Theme.of(context).textTheme.caption,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
await InstallPlugin.installApk(downloadFile.path, 'dev.imranr.obtainium');
|
await InstallPlugin.installApk(downloadFile.path, 'dev.imranr.obtainium');
|
||||||
|
|
||||||
apps[appId]!.app.installedVersion = apps[appId]!.app.latestVersion;
|
apps[appId]!.app.installedVersion = apps[appId]!.app.latestVersion;
|
||||||
saveApp(apps[appId]!.app);
|
await saveApp(apps[appId]!.app);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Directory> getAppsDir() async {
|
Future<Directory> getAppsDir() async {
|
||||||
@@ -236,6 +236,40 @@ class AppsProvider with ChangeNotifier {
|
|||||||
return updateAppIds;
|
return updateAppIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> 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<int> importApps(String appsJSON) async {
|
||||||
|
// FilePickerResult? result = await FilePicker.platform.pickFiles();
|
||||||
|
|
||||||
|
// if (result != null) {
|
||||||
|
// String appsJSON = File(result.files.single.path!).readAsStringSync();
|
||||||
|
List<App> importedApps = (jsonDecode(appsJSON) as List<dynamic>)
|
||||||
|
.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
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
IsolateNameServer.removePortNameMapping('downloader_send_port');
|
IsolateNameServer.removePortNameMapping('downloader_send_port');
|
||||||
|
|||||||
Reference in New Issue
Block a user