mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-11-01 05:53:27 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			231 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			231 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:flutter/material.dart';
 | |
| import 'package:flutter/services.dart';
 | |
| import 'package:obtainium/components/custom_app_bar.dart';
 | |
| import 'package:obtainium/components/generated_form.dart';
 | |
| import 'package:obtainium/custom_errors.dart';
 | |
| import 'package:obtainium/pages/app.dart';
 | |
| import 'package:obtainium/providers/apps_provider.dart';
 | |
| import 'package:obtainium/providers/settings_provider.dart';
 | |
| import 'package:obtainium/providers/source_provider.dart';
 | |
| import 'package:provider/provider.dart';
 | |
| import 'package:url_launcher/url_launcher_string.dart';
 | |
| 
 | |
| class AddAppPage extends StatefulWidget {
 | |
|   const AddAppPage({super.key});
 | |
| 
 | |
|   @override
 | |
|   State<AddAppPage> createState() => _AddAppPageState();
 | |
| }
 | |
| 
 | |
| class _AddAppPageState extends State<AddAppPage> {
 | |
|   bool gettingAppInfo = false;
 | |
| 
 | |
|   String userInput = '';
 | |
|   AppSource? pickedSource;
 | |
|   List<String> additionalData = [];
 | |
|   bool validAdditionalData = true;
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     SourceProvider sourceProvider = SourceProvider();
 | |
|     return Scaffold(
 | |
|         backgroundColor: Theme.of(context).colorScheme.surface,
 | |
|         body: CustomScrollView(slivers: <Widget>[
 | |
|           const CustomAppBar(title: 'Add App'),
 | |
|           SliverFillRemaining(
 | |
|             child: Padding(
 | |
|                 padding: const EdgeInsets.all(16),
 | |
|                 child: Column(
 | |
|                     crossAxisAlignment: CrossAxisAlignment.stretch,
 | |
|                     children: [
 | |
|                       Row(
 | |
|                         children: [
 | |
|                           Expanded(
 | |
|                               child: GeneratedForm(
 | |
|                                   items: [
 | |
|                                     [
 | |
|                                       GeneratedFormItem(
 | |
|                                           label: 'App Source Url',
 | |
|                                           additionalValidators: [
 | |
|                                             (value) {
 | |
|                                               try {
 | |
|                                                 sourceProvider
 | |
|                                                     .getSource(value ?? '')
 | |
|                                                     .standardizeURL(
 | |
|                                                         preStandardizeUrl(
 | |
|                                                             value ?? ''));
 | |
|                                               } catch (e) {
 | |
|                                                 return e is String
 | |
|                                                     ? e
 | |
|                                                     : e is ObtainiumError
 | |
|                                                         ? e.toString()
 | |
|                                                         : 'Error';
 | |
|                                               }
 | |
|                                               return null;
 | |
|                                             }
 | |
|                                           ])
 | |
|                                     ]
 | |
|                                   ],
 | |
|                                   onValueChanges: (values, valid, isBuilding) {
 | |
|                                     setState(() {
 | |
|                                       userInput = values[0];
 | |
|                                       var source = valid
 | |
|                                           ? sourceProvider.getSource(userInput)
 | |
|                                           : null;
 | |
|                                       if (pickedSource != source) {
 | |
|                                         pickedSource = source;
 | |
|                                         additionalData = source != null
 | |
|                                             ? source.additionalDataDefaults
 | |
|                                             : [];
 | |
|                                         validAdditionalData = source != null
 | |
|                                             ? sourceProvider
 | |
|                                                 .ifSourceAppsRequireAdditionalData(
 | |
|                                                     source)
 | |
|                                             : true;
 | |
|                                       }
 | |
|                                     });
 | |
|                                   },
 | |
|                                   defaultValues: const [])),
 | |
|                           const SizedBox(
 | |
|                             width: 16,
 | |
|                           ),
 | |
|                           gettingAppInfo
 | |
|                               ? const CircularProgressIndicator()
 | |
|                               : ElevatedButton(
 | |
|                                   onPressed: gettingAppInfo ||
 | |
|                                           pickedSource == null ||
 | |
|                                           (pickedSource!.additionalDataFormItems
 | |
|                                                   .isNotEmpty &&
 | |
|                                               !validAdditionalData)
 | |
|                                       ? null
 | |
|                                       : () async {
 | |
|                                           setState(() {
 | |
|                                             gettingAppInfo = true;
 | |
|                                           });
 | |
|                                           var appsProvider =
 | |
|                                               context.read<AppsProvider>();
 | |
|                                           var settingsProvider =
 | |
|                                               context.read<SettingsProvider>();
 | |
|                                           () async {
 | |
|                                             HapticFeedback.selectionClick();
 | |
|                                             App app =
 | |
|                                                 await sourceProvider.getApp(
 | |
|                                                     pickedSource!,
 | |
|                                                     userInput,
 | |
|                                                     additionalData);
 | |
|                                             await settingsProvider
 | |
|                                                 .getInstallPermission();
 | |
|                                             // Only download the APK here if you need to for the package ID
 | |
|                                             if (sourceProvider
 | |
|                                                 .isTempId(app.id)) {
 | |
|                                               // ignore: use_build_context_synchronously
 | |
|                                               var apkUrl = await appsProvider
 | |
|                                                   .confirmApkUrl(app, context);
 | |
|                                               if (apkUrl == null) {
 | |
|                                                 throw ObtainiumError(
 | |
|                                                     'Cancelled');
 | |
|                                               }
 | |
|                                               app.preferredApkIndex =
 | |
|                                                   app.apkUrls.indexOf(apkUrl);
 | |
|                                               var downloadedApk =
 | |
|                                                   await appsProvider
 | |
|                                                       .downloadApp(app);
 | |
|                                               app.id = downloadedApk.appId;
 | |
|                                             }
 | |
|                                             if (appsProvider.apps
 | |
|                                                 .containsKey(app.id)) {
 | |
|                                               throw ObtainiumError(
 | |
|                                                   'App already added');
 | |
|                                             }
 | |
|                                             await appsProvider.saveApps([app]);
 | |
| 
 | |
|                                             return app;
 | |
|                                           }()
 | |
|                                               .then((app) {
 | |
|                                             Navigator.push(
 | |
|                                                 context,
 | |
|                                                 MaterialPageRoute(
 | |
|                                                     builder: (context) =>
 | |
|                                                         AppPage(
 | |
|                                                             appId: app.id)));
 | |
|                                           }).catchError((e) {
 | |
|                                             showError(e, context);
 | |
|                                           }).whenComplete(() {
 | |
|                                             setState(() {
 | |
|                                               gettingAppInfo = false;
 | |
|                                             });
 | |
|                                           });
 | |
|                                         },
 | |
|                                   child: const Text('Add'))
 | |
|                         ],
 | |
|                       ),
 | |
|                       if (pickedSource != null &&
 | |
|                           pickedSource!.additionalDataDefaults.isNotEmpty)
 | |
|                         Column(
 | |
|                           crossAxisAlignment: CrossAxisAlignment.stretch,
 | |
|                           children: [
 | |
|                             const Divider(
 | |
|                               height: 64,
 | |
|                             ),
 | |
|                             Text(
 | |
|                                 'Additional Options for ${pickedSource?.runtimeType}',
 | |
|                                 style: TextStyle(
 | |
|                                     color:
 | |
|                                         Theme.of(context).colorScheme.primary)),
 | |
|                             const SizedBox(
 | |
|                               height: 16,
 | |
|                             ),
 | |
|                             if (pickedSource!
 | |
|                                 .additionalDataFormItems.isNotEmpty)
 | |
|                               GeneratedForm(
 | |
|                                   items: pickedSource!.additionalDataFormItems,
 | |
|                                   onValueChanges: (values, valid, isBuilding) {
 | |
|                                     setState(() {
 | |
|                                       additionalData = values;
 | |
|                                       validAdditionalData = valid;
 | |
|                                     });
 | |
|                                   },
 | |
|                                   defaultValues:
 | |
|                                       pickedSource!.additionalDataDefaults),
 | |
|                             if (pickedSource!
 | |
|                                 .additionalDataFormItems.isNotEmpty)
 | |
|                               const SizedBox(
 | |
|                                 height: 8,
 | |
|                               ),
 | |
|                           ],
 | |
|                         )
 | |
|                       else
 | |
|                         Expanded(
 | |
|                             child: Column(
 | |
|                                 crossAxisAlignment: CrossAxisAlignment.center,
 | |
|                                 mainAxisAlignment: MainAxisAlignment.center,
 | |
|                                 children: [
 | |
|                               const Text(
 | |
|                                 'Supported Sources:',
 | |
|                               ),
 | |
|                               const SizedBox(
 | |
|                                 height: 8,
 | |
|                               ),
 | |
|                               ...sourceProvider
 | |
|                                   .getSourceHosts()
 | |
|                                   .map((e) => GestureDetector(
 | |
|                                       onTap: () {
 | |
|                                         launchUrlString('https://$e',
 | |
|                                             mode:
 | |
|                                                 LaunchMode.externalApplication);
 | |
|                                       },
 | |
|                                       child: Text(
 | |
|                                         e,
 | |
|                                         style: const TextStyle(
 | |
|                                             decoration:
 | |
|                                                 TextDecoration.underline,
 | |
|                                             fontStyle: FontStyle.italic),
 | |
|                                       )))
 | |
|                                   .toList()
 | |
|                             ])),
 | |
|                     ])),
 | |
|           )
 | |
|         ]));
 | |
|   }
 | |
| }
 |