mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-31 13:33:28 +01:00 
			
		
		
		
	More progress on UI - basics almost done
This commit is contained in:
		| @@ -22,67 +22,6 @@ class MyApp extends StatelessWidget { | |||||||
|         theme: ThemeData( |         theme: ThemeData( | ||||||
|           primarySwatch: Colors.blue, |           primarySwatch: Colors.blue, | ||||||
|         ), |         ), | ||||||
|         // home: const MyHomePage(title: 'Obtainium'), |  | ||||||
|         home: const AppsPage()); |         home: const AppsPage()); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| // class MyHomePage extends StatefulWidget { |  | ||||||
| //   const MyHomePage({super.key, required this.title}); |  | ||||||
|  |  | ||||||
| //   final String title; |  | ||||||
|  |  | ||||||
| //   @override |  | ||||||
| //   State<MyHomePage> createState() => _MyHomePageState(); |  | ||||||
| // } |  | ||||||
|  |  | ||||||
| // class _MyHomePageState extends State<MyHomePage> { |  | ||||||
| //   int ind = 0; |  | ||||||
| //   List<String> urls = [ |  | ||||||
| //     'https://github.com/Ashinch/ReadYou/releases/download', // Should work |  | ||||||
| //     'http://github.com/syncthing/syncthing-android/releases/tag/1.20.4', // Should work |  | ||||||
| //     'https://github.com/videolan/vlc' // Should not |  | ||||||
| //   ]; |  | ||||||
|  |  | ||||||
| //   @override |  | ||||||
| //   Widget build(BuildContext context) { |  | ||||||
| //     ToastContext().init(context); |  | ||||||
| //     return Scaffold( |  | ||||||
| //       appBar: AppBar( |  | ||||||
| //         title: Text(widget.title), |  | ||||||
| //       ), |  | ||||||
| //       body: Center( |  | ||||||
| //         child: Column( |  | ||||||
| //           mainAxisAlignment: MainAxisAlignment.center, |  | ||||||
| //           children: <Widget>[ |  | ||||||
| //             Text( |  | ||||||
| //               urls[ind], |  | ||||||
| //               style: Theme.of(context).textTheme.headline4, |  | ||||||
| //             ), |  | ||||||
| //           ], |  | ||||||
| //         ), |  | ||||||
| //       ), |  | ||||||
| //       floatingActionButton: FloatingActionButton( |  | ||||||
| //         onPressed: () { |  | ||||||
| //           context.read<AppsProvider>().installApp(urls[ind]).then((_) { |  | ||||||
| //             setState(() { |  | ||||||
| //               ind = ind == (urls.length - 1) ? 0 : ind + 1; |  | ||||||
| //             }); |  | ||||||
| //           }).catchError((err) { |  | ||||||
| //             if (err is! String) { |  | ||||||
| //               err = "Unknown Error"; |  | ||||||
| //             } |  | ||||||
| //             Toast.show(err); |  | ||||||
| //           }); |  | ||||||
| //         }, |  | ||||||
| //         tooltip: 'Increment', |  | ||||||
| //         child: const Icon(Icons.add), |  | ||||||
| //       ), |  | ||||||
| //     ); |  | ||||||
| //   } |  | ||||||
|  |  | ||||||
| //   @override |  | ||||||
| //   void dispose() { |  | ||||||
| //     super.dispose(); |  | ||||||
| //   } |  | ||||||
| // } |  | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ class AddAppPage extends StatefulWidget { | |||||||
| class _AddAppPageState extends State<AddAppPage> { | class _AddAppPageState extends State<AddAppPage> { | ||||||
|   final _formKey = GlobalKey<FormState>(); |   final _formKey = GlobalKey<FormState>(); | ||||||
|   final urlInputController = TextEditingController(); |   final urlInputController = TextEditingController(); | ||||||
|  |   bool gettingAppInfo = false; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
| @@ -25,45 +26,67 @@ class _AddAppPageState extends State<AddAppPage> { | |||||||
|           child: Form( |           child: Form( | ||||||
|         key: _formKey, |         key: _formKey, | ||||||
|         child: Column( |         child: Column( | ||||||
|           crossAxisAlignment: CrossAxisAlignment.start, |           crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|           mainAxisAlignment: MainAxisAlignment.center, |  | ||||||
|           children: [ |           children: [ | ||||||
|             TextFormField( |             const Spacer(), | ||||||
|               controller: urlInputController, |  | ||||||
|               validator: (value) { |  | ||||||
|                 if (value == null || |  | ||||||
|                     value.isEmpty || |  | ||||||
|                     Uri.tryParse(value) == null) { |  | ||||||
|                   return 'Please enter a supported source URL'; |  | ||||||
|                 } |  | ||||||
|                 return null; |  | ||||||
|               }, |  | ||||||
|             ), |  | ||||||
|             Padding( |             Padding( | ||||||
|               padding: const EdgeInsets.symmetric(vertical: 16.0), |                 padding: const EdgeInsets.symmetric(horizontal: 16.0), | ||||||
|  |                 child: TextFormField( | ||||||
|  |                   decoration: const InputDecoration( | ||||||
|  |                       border: OutlineInputBorder(), | ||||||
|  |                       hintText: 'https://github.com/Author/Project', | ||||||
|  |                       helperText: 'Enter the App source URL'), | ||||||
|  |                   controller: urlInputController, | ||||||
|  |                   validator: (value) { | ||||||
|  |                     if (value == null || | ||||||
|  |                         value.isEmpty || | ||||||
|  |                         Uri.tryParse(value) == null) { | ||||||
|  |                       return 'Please enter a supported source URL'; | ||||||
|  |                     } | ||||||
|  |                     return null; | ||||||
|  |                   }, | ||||||
|  |                 )), | ||||||
|  |             Padding( | ||||||
|  |               padding: | ||||||
|  |                   const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), | ||||||
|               child: ElevatedButton( |               child: ElevatedButton( | ||||||
|                 onPressed: () { |                 onPressed: gettingAppInfo | ||||||
|                   if (_formKey.currentState!.validate()) { |                     ? null | ||||||
|                     SourceService() |                     : () { | ||||||
|                         .getApp(urlInputController.value.text) |                         if (_formKey.currentState!.validate()) { | ||||||
|                         .then((app) { |                           setState(() { | ||||||
|                       var appsProvider = context.read<AppsProvider>(); |                             gettingAppInfo = true; | ||||||
|                       appsProvider.saveApp(app).then((_) { |                           }); | ||||||
|                         Navigator.push( |                           SourceService() | ||||||
|                             context, |                               .getApp(urlInputController.value.text) | ||||||
|                             MaterialPageRoute( |                               .then((app) { | ||||||
|                                 builder: (context) => AppPage(appId: app.id))); |                             var appsProvider = context.read<AppsProvider>(); | ||||||
|                       }); |                             if (appsProvider.apps.containsKey(app.id)) { | ||||||
|                     }).catchError((e) { |                               throw 'App already added'; | ||||||
|                       ScaffoldMessenger.of(context).showSnackBar( |                             } | ||||||
|                         SnackBar(content: Text(e.toString())), |                             appsProvider.saveApp(app).then((_) { | ||||||
|                       ); |                               Navigator.push( | ||||||
|                     }); |                                   context, | ||||||
|                   } |                                   MaterialPageRoute( | ||||||
|                 }, |                                       builder: (context) => | ||||||
|  |                                           AppPage(appId: app.id))); | ||||||
|  |                             }); | ||||||
|  |                           }).catchError((e) { | ||||||
|  |                             ScaffoldMessenger.of(context).showSnackBar( | ||||||
|  |                               SnackBar(content: Text(e.toString())), | ||||||
|  |                             ); | ||||||
|  |                           }).whenComplete(() { | ||||||
|  |                             setState(() { | ||||||
|  |                               gettingAppInfo = false; | ||||||
|  |                             }); | ||||||
|  |                           }); | ||||||
|  |                         } | ||||||
|  |                       }, | ||||||
|                 child: const Text('Add'), |                 child: const Text('Add'), | ||||||
|               ), |               ), | ||||||
|             ), |             ), | ||||||
|  |             const Spacer(), | ||||||
|  |             if (gettingAppInfo) const LinearProgressIndicator(), | ||||||
|           ], |           ], | ||||||
|         ), |         ), | ||||||
|       )), |       )), | ||||||
|   | |||||||
| @@ -18,15 +18,68 @@ class _AppPageState extends State<AppPage> { | |||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     var appsProvider = context.watch<AppsProvider>(); |     var appsProvider = context.watch<AppsProvider>(); | ||||||
|     App? app = appsProvider.apps[widget.appId]; |     App? app = appsProvider.apps[widget.appId]; | ||||||
|     if (app == null) { |     if (app?.installedVersion != null) { | ||||||
|       Navigator.pop(context); |       appsProvider.getUpdate(app!.id); | ||||||
|     } |     } | ||||||
|     return Scaffold( |     return Scaffold( | ||||||
|         appBar: AppBar( |       appBar: AppBar( | ||||||
|           title: Text('App - ${app?.name} - ${app?.author}'), |         title: Text('${app?.author}/${app?.name}'), | ||||||
|         ), |       ), | ||||||
|         body: WebView( |       body: WebView( | ||||||
|           initialUrl: app?.url, |         initialUrl: app?.url, | ||||||
|         )); |       ), | ||||||
|  |       bottomSheet: Padding( | ||||||
|  |           padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), | ||||||
|  |           child: | ||||||
|  |               Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ | ||||||
|  |             Expanded( | ||||||
|  |                 child: OutlinedButton( | ||||||
|  |                     onPressed: (app?.installedVersion == null || | ||||||
|  |                                 appsProvider.checkAppObjectForUpdate(app!)) && | ||||||
|  |                             app?.currentDownloadId == null | ||||||
|  |                         ? () { | ||||||
|  |                             appsProvider.backgroundDownloadAndInstallApp(app!); | ||||||
|  |                           } | ||||||
|  |                         : null, | ||||||
|  |                     child: Text( | ||||||
|  |                         app?.installedVersion == null ? 'Install' : 'Update'))), | ||||||
|  |             const SizedBox(width: 16.0), | ||||||
|  |             OutlinedButton( | ||||||
|  |               onPressed: app?.currentDownloadId != null | ||||||
|  |                   ? null | ||||||
|  |                   : () { | ||||||
|  |                       showDialog( | ||||||
|  |                           context: context, | ||||||
|  |                           builder: (BuildContext ctx) { | ||||||
|  |                             return AlertDialog( | ||||||
|  |                               title: const Text('Remove App?'), | ||||||
|  |                               content: Text( | ||||||
|  |                                   'This will remove \'${app?.name}\' from Obtainium.${app?.installedVersion != null ? '\n\nNote that while Obtainium will no longer track its updates, the App will remain installed.' : ''}'), | ||||||
|  |                               actions: [ | ||||||
|  |                                 TextButton( | ||||||
|  |                                     onPressed: () { | ||||||
|  |                                       appsProvider.removeApp(app!.id).then((_) { | ||||||
|  |                                         int count = 0; | ||||||
|  |                                         Navigator.of(context) | ||||||
|  |                                             .popUntil((_) => count++ >= 2); | ||||||
|  |                                       }); | ||||||
|  |                                     }, | ||||||
|  |                                     child: const Text('Remove')), | ||||||
|  |                                 TextButton( | ||||||
|  |                                     onPressed: () { | ||||||
|  |                                       Navigator.of(context).pop(); | ||||||
|  |                                     }, | ||||||
|  |                                     child: const Text('Cancel')) | ||||||
|  |                               ], | ||||||
|  |                             ); | ||||||
|  |                           }); | ||||||
|  |                     }, | ||||||
|  |               style: TextButton.styleFrom( | ||||||
|  |                   foregroundColor: Theme.of(context).errorColor), | ||||||
|  |               child: const Text('Remove'), | ||||||
|  |             ) | ||||||
|  |             // TODO: Add progress bar when app?.currentDownloadId != null | ||||||
|  |           ])), | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:obtainium/pages/add_app.dart'; | import 'package:obtainium/pages/add_app.dart'; | ||||||
|  | import 'package:obtainium/pages/app.dart'; | ||||||
| import 'package:obtainium/services/apps_provider.dart'; | import 'package:obtainium/services/apps_provider.dart'; | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
|  |  | ||||||
| @@ -13,24 +14,39 @@ class AppsPage extends StatefulWidget { | |||||||
| class _AppsPageState extends State<AppsPage> { | class _AppsPageState extends State<AppsPage> { | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|  |     var appsProvider = context.watch<AppsProvider>(); | ||||||
|  |  | ||||||
|     return Scaffold( |     return Scaffold( | ||||||
|       appBar: AppBar( |       appBar: AppBar( | ||||||
|         title: const Text('Obtainium - Apps'), |         title: const Text('Obtainium'), | ||||||
|       ), |       ), | ||||||
|       body: Center( |       body: Center( | ||||||
|         child: Column( |         child: appsProvider.loadingApps | ||||||
|           mainAxisAlignment: MainAxisAlignment.center, |             ? const CircularProgressIndicator() | ||||||
|           children: () { |             : appsProvider.apps.isEmpty | ||||||
|             var appsProvider = context.watch<AppsProvider>(); |                 ? Text( | ||||||
|             if (appsProvider.loadingApps) { |                     'No Apps', | ||||||
|               return [const Text('Loading Apps...')]; |                     style: Theme.of(context).textTheme.headline4, | ||||||
|             } else if (appsProvider.apps.isEmpty) { |                   ) | ||||||
|               return [const Text('No Apps Yet.')]; |                 : ListView( | ||||||
|             } else { |                     children: appsProvider.apps.values | ||||||
|               return appsProvider.apps.values.map((e) => Text(e.id)).toList(); |                         .map( | ||||||
|             } |                           (e) => ListTile( | ||||||
|           }(), |                             title: Text(e.name), | ||||||
|         ), |                             subtitle: Text(e.author), | ||||||
|  |                             trailing: | ||||||
|  |                                 Text(e.installedVersion ?? 'Not Installed'), | ||||||
|  |                             onTap: () { | ||||||
|  |                               Navigator.push( | ||||||
|  |                                 context, | ||||||
|  |                                 MaterialPageRoute( | ||||||
|  |                                     builder: (context) => AppPage(appId: e.id)), | ||||||
|  |                               ); | ||||||
|  |                             }, | ||||||
|  |                           ), | ||||||
|  |                         ) | ||||||
|  |                         .toList(), | ||||||
|  |                   ), | ||||||
|       ), |       ), | ||||||
|       floatingActionButton: FloatingActionButton( |       floatingActionButton: FloatingActionButton( | ||||||
|         onPressed: () { |         onPressed: () { | ||||||
|   | |||||||
| @@ -92,9 +92,9 @@ class AppsProvider with ChangeNotifier { | |||||||
|       } |       } | ||||||
|       // Change App status to no longer downloading |       // Change App status to no longer downloading | ||||||
|       App? foundApp; |       App? foundApp; | ||||||
|       apps.forEach((id, app) { |       apps.forEach((appId, app) { | ||||||
|         if (app.currentDownloadId == id) { |         if (app.currentDownloadId == id) { | ||||||
|           foundApp = apps[app.id]; |           foundApp = apps[appId]; | ||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
|       foundApp!.currentDownloadId = null; |       foundApp!.currentDownloadId = null; | ||||||
| @@ -112,7 +112,7 @@ class AppsProvider with ChangeNotifier { | |||||||
|     if (apkDir.existsSync()) apkDir.deleteSync(recursive: true); |     if (apkDir.existsSync()) apkDir.deleteSync(recursive: true); | ||||||
|     apkDir.createSync(recursive: true); |     apkDir.createSync(recursive: true); | ||||||
|     String? downloadId = await FlutterDownloader.enqueue( |     String? downloadId = await FlutterDownloader.enqueue( | ||||||
|       url: app.url, |       url: app.apkUrl, | ||||||
|       savedDir: apkDir.path, |       savedDir: apkDir.path, | ||||||
|       showNotification: true, |       showNotification: true, | ||||||
|       openFileFromNotification: false, |       openFileFromNotification: false, | ||||||
| @@ -158,23 +158,42 @@ class AppsProvider with ChangeNotifier { | |||||||
|     notifyListeners(); |     notifyListeners(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool checkUpdate(App app) { |   Future<void> removeApp(String appId) async { | ||||||
|  |     File file = File('${(await getAppsDir()).path}/$appId.json'); | ||||||
|  |     if (file.existsSync()) { | ||||||
|  |       file.deleteSync(); | ||||||
|  |     } | ||||||
|  |     if (apps.containsKey(appId)) { | ||||||
|  |       apps.remove(appId); | ||||||
|  |     } | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   bool checkAppObjectForUpdate(App app) { | ||||||
|     if (!apps.containsKey(app.id)) { |     if (!apps.containsKey(app.id)) { | ||||||
|       throw 'App not found'; |       throw 'App not found'; | ||||||
|     } |     } | ||||||
|     return app.latestVersion != apps[app.id]?.installedVersion; |     return app.latestVersion != apps[app.id]?.installedVersion; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<List<App>> checkUpdates() async { |   Future<App?> getUpdate(String appId) async { | ||||||
|  |     App? currentApp = apps[appId]; | ||||||
|  |     App newApp = await SourceService().getApp(currentApp!.url); | ||||||
|  |     if (newApp.latestVersion != currentApp.latestVersion) { | ||||||
|  |       newApp.installedVersion = currentApp.installedVersion; | ||||||
|  |       await saveApp(newApp); | ||||||
|  |       return newApp; | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Future<List<App>> getUpdates() async { | ||||||
|     List<App> updates = []; |     List<App> updates = []; | ||||||
|     List<String> appIds = apps.keys.toList(); |     List<String> appIds = apps.keys.toList(); | ||||||
|     for (int i = 0; i < appIds.length; i++) { |     for (int i = 0; i < appIds.length; i++) { | ||||||
|       App? currentApp = apps[appIds[i]]; |       App? newApp = await getUpdate(appIds[i]); | ||||||
|       App newApp = await SourceService().getApp(currentApp!.url); |       if (newApp != null) { | ||||||
|       if (newApp.latestVersion != currentApp.latestVersion) { |  | ||||||
|         newApp.installedVersion = currentApp.installedVersion; |  | ||||||
|         updates.add(newApp); |         updates.add(newApp); | ||||||
|         await saveApp(newApp); |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     return updates; |     return updates; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user