mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-15 06:06:44 +02:00
Compare commits
3 Commits
v0.2.2-bet
...
v0.2.4-bet
Author | SHA1 | Date | |
---|---|---|---|
6c1ad94b4f | |||
7d7986f8bf | |||
3ddf9ea736 |
@ -12,7 +12,7 @@ import 'package:dynamic_color/dynamic_color.dart';
|
|||||||
import 'package:device_info_plus/device_info_plus.dart';
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
|
|
||||||
const String currentReleaseTag =
|
const String currentReleaseTag =
|
||||||
'v0.2.2-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
'v0.2.4-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
||||||
|
|
||||||
@pragma('vm:entry-point')
|
@pragma('vm:entry-point')
|
||||||
void bgTaskCallback() {
|
void bgTaskCallback() {
|
||||||
|
@ -23,125 +23,136 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
SourceProvider sourceProvider = SourceProvider();
|
SourceProvider sourceProvider = SourceProvider();
|
||||||
return CustomScrollView(slivers: <Widget>[
|
return Scaffold(
|
||||||
const CustomAppBar(title: 'Add App'),
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
SliverFillRemaining(
|
body: CustomScrollView(slivers: <Widget>[
|
||||||
hasScrollBody: false,
|
const CustomAppBar(title: 'Add App'),
|
||||||
child: Center(
|
SliverFillRemaining(
|
||||||
child: Form(
|
hasScrollBody: false,
|
||||||
key: _formKey,
|
child: Center(
|
||||||
child: Column(
|
child: Form(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
key: _formKey,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
child: Column(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
Container(),
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
Padding(
|
children: [
|
||||||
padding: const EdgeInsets.all(16),
|
Container(),
|
||||||
child: Column(
|
Padding(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
padding: const EdgeInsets.all(16),
|
||||||
children: [
|
child: Column(
|
||||||
TextFormField(
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
decoration: const InputDecoration(
|
children: [
|
||||||
hintText: 'https://github.com/Author/Project',
|
TextFormField(
|
||||||
helperText: 'Enter the App source URL'),
|
decoration: const InputDecoration(
|
||||||
controller: urlInputController,
|
hintText:
|
||||||
validator: (value) {
|
'https://github.com/Author/Project',
|
||||||
if (value == null ||
|
helperText: 'Enter the App source URL'),
|
||||||
value.isEmpty ||
|
controller: urlInputController,
|
||||||
Uri.tryParse(value) == null) {
|
validator: (value) {
|
||||||
return 'Please enter a supported source URL';
|
if (value == null ||
|
||||||
}
|
value.isEmpty ||
|
||||||
return null;
|
Uri.tryParse(value) == null) {
|
||||||
},
|
return 'Please enter a supported source URL';
|
||||||
),
|
}
|
||||||
Padding(
|
return null;
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
},
|
||||||
child: ElevatedButton(
|
),
|
||||||
onPressed: gettingAppInfo
|
Padding(
|
||||||
? null
|
padding:
|
||||||
: () {
|
const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
HapticFeedback.selectionClick();
|
child: ElevatedButton(
|
||||||
if (_formKey.currentState!.validate()) {
|
onPressed: gettingAppInfo
|
||||||
setState(() {
|
? null
|
||||||
gettingAppInfo = true;
|
: () {
|
||||||
});
|
HapticFeedback.selectionClick();
|
||||||
sourceProvider
|
if (_formKey.currentState!
|
||||||
.getApp(
|
.validate()) {
|
||||||
urlInputController.value.text)
|
setState(() {
|
||||||
.then((app) {
|
gettingAppInfo = true;
|
||||||
var appsProvider =
|
|
||||||
context.read<AppsProvider>();
|
|
||||||
var settingsProvider =
|
|
||||||
context.read<SettingsProvider>();
|
|
||||||
if (appsProvider.apps
|
|
||||||
.containsKey(app.id)) {
|
|
||||||
throw 'App already added';
|
|
||||||
}
|
|
||||||
settingsProvider
|
|
||||||
.getInstallPermission()
|
|
||||||
.then((_) {
|
|
||||||
appsProvider.saveApp(app).then((_) {
|
|
||||||
urlInputController.clear();
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
AppPage(
|
|
||||||
appId: app.id)));
|
|
||||||
});
|
});
|
||||||
});
|
sourceProvider
|
||||||
}).catchError((e) {
|
.getApp(urlInputController
|
||||||
ScaffoldMessenger.of(context)
|
.value.text)
|
||||||
.showSnackBar(
|
.then((app) {
|
||||||
SnackBar(
|
var appsProvider =
|
||||||
content: Text(e.toString())),
|
context.read<AppsProvider>();
|
||||||
);
|
var settingsProvider = context
|
||||||
}).whenComplete(() {
|
.read<SettingsProvider>();
|
||||||
setState(() {
|
if (appsProvider.apps
|
||||||
gettingAppInfo = false;
|
.containsKey(app.id)) {
|
||||||
});
|
throw 'App already added';
|
||||||
});
|
}
|
||||||
}
|
settingsProvider
|
||||||
},
|
.getInstallPermission()
|
||||||
child: const Text('Add'),
|
.then((_) {
|
||||||
),
|
appsProvider
|
||||||
|
.saveApp(app)
|
||||||
|
.then((_) {
|
||||||
|
urlInputController.clear();
|
||||||
|
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'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
Column(
|
||||||
),
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
const Text(
|
||||||
children: [
|
'Supported Sources:',
|
||||||
const Text(
|
// style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
'Supported Sources:',
|
// style: Theme.of(context).textTheme.bodySmall,
|
||||||
// style: TextStyle(fontWeight: FontWeight.bold),
|
),
|
||||||
// style: Theme.of(context).textTheme.bodySmall,
|
const SizedBox(
|
||||||
),
|
height: 8,
|
||||||
const SizedBox(
|
),
|
||||||
height: 8,
|
...sourceProvider
|
||||||
),
|
.getSourceHosts()
|
||||||
...sourceProvider
|
.map((e) => GestureDetector(
|
||||||
.getSourceHosts()
|
onTap: () {
|
||||||
.map((e) => GestureDetector(
|
launchUrlString('https://$e',
|
||||||
onTap: () {
|
mode:
|
||||||
launchUrlString('https://$e',
|
LaunchMode.externalApplication);
|
||||||
mode: LaunchMode.externalApplication);
|
},
|
||||||
},
|
child: Text(
|
||||||
child: Text(
|
e,
|
||||||
e,
|
style: const TextStyle(
|
||||||
style: const TextStyle(
|
decoration:
|
||||||
decoration: TextDecoration.underline,
|
TextDecoration.underline,
|
||||||
fontStyle: FontStyle.italic),
|
fontStyle: FontStyle.italic),
|
||||||
)))
|
)))
|
||||||
.toList()
|
.toList()
|
||||||
]),
|
]),
|
||||||
if (gettingAppInfo)
|
if (gettingAppInfo)
|
||||||
const LinearProgressIndicator()
|
const LinearProgressIndicator()
|
||||||
else
|
else
|
||||||
Container(),
|
Container(),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
]);
|
]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,74 +54,146 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CustomScrollView(slivers: <Widget>[
|
return Scaffold(
|
||||||
const CustomAppBar(title: 'Import/Export'),
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
SliverFillRemaining(
|
body: CustomScrollView(slivers: <Widget>[
|
||||||
hasScrollBody: false,
|
const CustomAppBar(title: 'Import/Export'),
|
||||||
child: Padding(
|
SliverFillRemaining(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
hasScrollBody: false,
|
||||||
child: Column(
|
child: Padding(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
padding:
|
||||||
children: [
|
const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||||
Row(
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Row(
|
||||||
child: TextButton(
|
children: [
|
||||||
style: outlineButtonStyle,
|
Expanded(
|
||||||
onPressed: appsProvider.apps.isEmpty ||
|
child: TextButton(
|
||||||
importInProgress
|
style: outlineButtonStyle,
|
||||||
? null
|
onPressed: appsProvider.apps.isEmpty ||
|
||||||
: () {
|
importInProgress
|
||||||
HapticFeedback.selectionClick();
|
? null
|
||||||
appsProvider
|
: () {
|
||||||
.exportApps()
|
HapticFeedback.selectionClick();
|
||||||
.then((String path) {
|
|
||||||
ScaffoldMessenger.of(context)
|
|
||||||
.showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content:
|
|
||||||
Text('Exported to $path')),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: const Text('Obtainium Export'))),
|
|
||||||
const SizedBox(
|
|
||||||
width: 16,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: TextButton(
|
|
||||||
style: outlineButtonStyle,
|
|
||||||
onPressed: importInProgress
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
HapticFeedback.selectionClick();
|
|
||||||
FilePicker.platform
|
|
||||||
.pickFiles()
|
|
||||||
.then((result) {
|
|
||||||
setState(() {
|
|
||||||
importInProgress = true;
|
|
||||||
});
|
|
||||||
if (result != null) {
|
|
||||||
String data =
|
|
||||||
File(result.files.single.path!)
|
|
||||||
.readAsStringSync();
|
|
||||||
try {
|
|
||||||
jsonDecode(data);
|
|
||||||
} catch (e) {
|
|
||||||
throw 'Invalid input';
|
|
||||||
}
|
|
||||||
appsProvider
|
appsProvider
|
||||||
.importApps(data)
|
.exportApps()
|
||||||
.then((value) {
|
.then((String path) {
|
||||||
ScaffoldMessenger.of(context)
|
ScaffoldMessenger.of(context)
|
||||||
.showSnackBar(
|
.showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(
|
content: Text(
|
||||||
'$value App${value == 1 ? '' : 's'} Imported')),
|
'Exported to $path')),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
child: const Text('Obtainium Export'))),
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TextButton(
|
||||||
|
style: outlineButtonStyle,
|
||||||
|
onPressed: importInProgress
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
HapticFeedback.selectionClick();
|
||||||
|
FilePicker.platform
|
||||||
|
.pickFiles()
|
||||||
|
.then((result) {
|
||||||
|
setState(() {
|
||||||
|
importInProgress = true;
|
||||||
|
});
|
||||||
|
if (result != null) {
|
||||||
|
String data = File(
|
||||||
|
result.files.single.path!)
|
||||||
|
.readAsStringSync();
|
||||||
|
try {
|
||||||
|
jsonDecode(data);
|
||||||
|
} catch (e) {
|
||||||
|
throw 'Invalid input';
|
||||||
|
}
|
||||||
|
appsProvider
|
||||||
|
.importApps(data)
|
||||||
|
.then((value) {
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
.showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
'$value App${value == 1 ? '' : 's'} Imported')),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// User canceled the picker
|
||||||
|
}
|
||||||
|
}).catchError((e) {
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
.showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(e.toString())),
|
||||||
|
);
|
||||||
|
}).whenComplete(() {
|
||||||
|
setState(() {
|
||||||
|
importInProgress = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: const Text('Obtainium Import')))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (importInProgress)
|
||||||
|
Column(
|
||||||
|
children: const [
|
||||||
|
SizedBox(
|
||||||
|
height: 14,
|
||||||
|
),
|
||||||
|
LinearProgressIndicator(),
|
||||||
|
SizedBox(
|
||||||
|
height: 14,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
else
|
||||||
|
const Divider(
|
||||||
|
height: 32,
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: importInProgress
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext ctx) {
|
||||||
|
return GeneratedFormModal(
|
||||||
|
title: 'Import from URL List',
|
||||||
|
items: [
|
||||||
|
GeneratedFormItem(
|
||||||
|
'App URL List', true, 7)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}).then((values) {
|
||||||
|
if (values != null) {
|
||||||
|
var urls =
|
||||||
|
(values[0] as String).split('\n');
|
||||||
|
setState(() {
|
||||||
|
importInProgress = true;
|
||||||
|
});
|
||||||
|
addApps(urls).then((errors) {
|
||||||
|
if (errors.isEmpty) {
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
.showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
'Imported ${urls.length} Apps')),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// User canceled the picker
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext ctx) {
|
||||||
|
return ImportErrorDialog(
|
||||||
|
urlsLength: urls.length,
|
||||||
|
errors: errors);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
ScaffoldMessenger.of(context)
|
ScaffoldMessenger.of(context)
|
||||||
@ -133,148 +205,92 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
importInProgress = false;
|
importInProgress = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
|
||||||
child: const Text('Obtainium Import')))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (importInProgress)
|
|
||||||
Column(
|
|
||||||
children: const [
|
|
||||||
SizedBox(
|
|
||||||
height: 14,
|
|
||||||
),
|
|
||||||
LinearProgressIndicator(),
|
|
||||||
SizedBox(
|
|
||||||
height: 14,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
else
|
|
||||||
const Divider(
|
|
||||||
height: 32,
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: importInProgress
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext ctx) {
|
|
||||||
return GeneratedFormModal(
|
|
||||||
title: 'Import from URL List',
|
|
||||||
items: [
|
|
||||||
GeneratedFormItem(
|
|
||||||
'App URL List', true, 7)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}).then((values) {
|
|
||||||
if (values != null) {
|
|
||||||
var urls = (values[0] as String).split('\n');
|
|
||||||
setState(() {
|
|
||||||
importInProgress = true;
|
|
||||||
});
|
|
||||||
addApps(urls).then((errors) {
|
|
||||||
if (errors.isEmpty) {
|
|
||||||
ScaffoldMessenger.of(context)
|
|
||||||
.showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(
|
|
||||||
'Imported ${urls.length} Apps')),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext ctx) {
|
|
||||||
return ImportErrorDialog(
|
|
||||||
urlsLength: urls.length,
|
|
||||||
errors: errors);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}).catchError((e) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text(e.toString())),
|
|
||||||
);
|
|
||||||
}).whenComplete(() {
|
|
||||||
setState(() {
|
|
||||||
importInProgress = false;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
child: const Text(
|
||||||
},
|
'Import from URL List',
|
||||||
child: const Text(
|
)),
|
||||||
'Import from URL List',
|
...sourceProvider.massSources
|
||||||
)),
|
.map((source) => Column(
|
||||||
...sourceProvider.massSources
|
crossAxisAlignment:
|
||||||
.map((source) => Column(
|
CrossAxisAlignment.stretch,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
children: [
|
||||||
children: [
|
const SizedBox(height: 8),
|
||||||
const SizedBox(height: 8),
|
TextButton(
|
||||||
TextButton(
|
onPressed: importInProgress
|
||||||
onPressed: importInProgress
|
? null
|
||||||
? null
|
: () {
|
||||||
: () {
|
showDialog(
|
||||||
showDialog(
|
context: context,
|
||||||
context: context,
|
builder:
|
||||||
builder: (BuildContext ctx) {
|
(BuildContext ctx) {
|
||||||
return GeneratedFormModal(
|
return GeneratedFormModal(
|
||||||
title:
|
title:
|
||||||
'Import ${source.name}',
|
'Import ${source.name}',
|
||||||
items: source.requiredArgs
|
items: source
|
||||||
.map((e) =>
|
.requiredArgs
|
||||||
GeneratedFormItem(
|
.map((e) =>
|
||||||
e, true, 1))
|
GeneratedFormItem(
|
||||||
.toList());
|
e,
|
||||||
}).then((values) {
|
true,
|
||||||
if (values != null) {
|
1))
|
||||||
source
|
.toList());
|
||||||
.getUrls(values)
|
}).then((values) {
|
||||||
.then((urls) {
|
if (values != null) {
|
||||||
setState(() {
|
source
|
||||||
importInProgress = true;
|
.getUrls(values)
|
||||||
});
|
.then((urls) {
|
||||||
addApps(urls).then((errors) {
|
setState(() {
|
||||||
if (errors.isEmpty) {
|
importInProgress = true;
|
||||||
|
});
|
||||||
|
addApps(urls)
|
||||||
|
.then((errors) {
|
||||||
|
if (errors.isEmpty) {
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context)
|
||||||
|
.showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
'Imported ${urls.length} Apps')),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder:
|
||||||
|
(BuildContext
|
||||||
|
ctx) {
|
||||||
|
return ImportErrorDialog(
|
||||||
|
urlsLength: urls
|
||||||
|
.length,
|
||||||
|
errors:
|
||||||
|
errors);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).whenComplete(() {
|
||||||
|
setState(() {
|
||||||
|
importInProgress =
|
||||||
|
false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).catchError((e) {
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(
|
||||||
context)
|
context)
|
||||||
.showSnackBar(
|
.showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(
|
content: Text(
|
||||||
'Imported ${urls.length} Apps')),
|
e.toString())),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext
|
|
||||||
ctx) {
|
|
||||||
return ImportErrorDialog(
|
|
||||||
urlsLength:
|
|
||||||
urls.length,
|
|
||||||
errors: errors);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).whenComplete(() {
|
|
||||||
setState(() {
|
|
||||||
importInProgress = false;
|
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}).catchError((e) {
|
|
||||||
ScaffoldMessenger.of(context)
|
|
||||||
.showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content:
|
|
||||||
Text(e.toString())),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
child: Text('Import ${source.name}'))
|
||||||
},
|
]))
|
||||||
child: Text('Import ${source.name}'))
|
.toList()
|
||||||
]))
|
],
|
||||||
.toList()
|
)))
|
||||||
],
|
]));
|
||||||
)))
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:obtainium/components/custom_app_bar.dart';
|
import 'package:obtainium/components/custom_app_bar.dart';
|
||||||
import 'package:obtainium/providers/settings_provider.dart';
|
import 'package:obtainium/providers/settings_provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -19,214 +18,219 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
if (settingsProvider.prefs == null) {
|
if (settingsProvider.prefs == null) {
|
||||||
settingsProvider.initializeSettings();
|
settingsProvider.initializeSettings();
|
||||||
}
|
}
|
||||||
return CustomScrollView(slivers: <Widget>[
|
return Scaffold(
|
||||||
const CustomAppBar(title: 'Add App'),
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
SliverFillRemaining(
|
body: CustomScrollView(slivers: <Widget>[
|
||||||
hasScrollBody: true,
|
const CustomAppBar(title: 'Settings'),
|
||||||
child: Padding(
|
SliverFillRemaining(
|
||||||
padding: const EdgeInsets.all(16),
|
hasScrollBody: true,
|
||||||
child: settingsProvider.prefs == null
|
child: Padding(
|
||||||
? Container()
|
padding: const EdgeInsets.all(16),
|
||||||
: Column(
|
child: settingsProvider.prefs == null
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
? Container()
|
||||||
children: [
|
: Column(
|
||||||
Text(
|
|
||||||
'Appearance',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.primary),
|
|
||||||
),
|
|
||||||
DropdownButtonFormField(
|
|
||||||
decoration:
|
|
||||||
const InputDecoration(labelText: 'Theme'),
|
|
||||||
value: settingsProvider.theme,
|
|
||||||
items: const [
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: ThemeSettings.dark,
|
|
||||||
child: Text('Dark'),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: ThemeSettings.light,
|
|
||||||
child: Text('Light'),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: ThemeSettings.system,
|
|
||||||
child: Text('Follow System'),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
settingsProvider.theme = value;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
const SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
DropdownButtonFormField(
|
|
||||||
decoration:
|
|
||||||
const InputDecoration(labelText: 'Colour'),
|
|
||||||
value: settingsProvider.colour,
|
|
||||||
items: const [
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: ColourSettings.basic,
|
|
||||||
child: Text('Obtainium'),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: ColourSettings.materialYou,
|
|
||||||
child: Text('Material You'),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
settingsProvider.colour = value;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
const SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Text(
|
||||||
child: DropdownButtonFormField(
|
'Appearance',
|
||||||
decoration: const InputDecoration(
|
style: TextStyle(
|
||||||
labelText: 'App Sort By'),
|
color: Theme.of(context).colorScheme.primary),
|
||||||
value: settingsProvider.sortColumn,
|
|
||||||
items: const [
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: SortColumnSettings.authorName,
|
|
||||||
child: Text('Author/Name'),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: SortColumnSettings.nameAuthor,
|
|
||||||
child: Text('Name/Author'),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: SortColumnSettings.added,
|
|
||||||
child: Text('As Added'),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
settingsProvider.sortColumn = value;
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
const SizedBox(
|
|
||||||
width: 16,
|
|
||||||
),
|
),
|
||||||
Expanded(
|
DropdownButtonFormField(
|
||||||
child: DropdownButtonFormField(
|
decoration:
|
||||||
decoration: const InputDecoration(
|
const InputDecoration(labelText: 'Theme'),
|
||||||
labelText: 'App Sort Order'),
|
value: settingsProvider.theme,
|
||||||
value: settingsProvider.sortOrder,
|
items: const [
|
||||||
items: const [
|
DropdownMenuItem(
|
||||||
DropdownMenuItem(
|
value: ThemeSettings.dark,
|
||||||
value: SortOrderSettings.ascending,
|
child: Text('Dark'),
|
||||||
child: Text('Ascending'),
|
),
|
||||||
),
|
DropdownMenuItem(
|
||||||
DropdownMenuItem(
|
value: ThemeSettings.light,
|
||||||
value: SortOrderSettings.descending,
|
child: Text('Light'),
|
||||||
child: Text('Descending'),
|
),
|
||||||
),
|
DropdownMenuItem(
|
||||||
],
|
value: ThemeSettings.system,
|
||||||
onChanged: (value) {
|
child: Text('Follow System'),
|
||||||
if (value != null) {
|
)
|
||||||
settingsProvider.sortOrder = value;
|
],
|
||||||
}
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Text('Show Source Webpage in App View'),
|
|
||||||
Switch(
|
|
||||||
value: settingsProvider.showAppWebpage,
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
settingsProvider.showAppWebpage = value;
|
if (value != null) {
|
||||||
})
|
settingsProvider.theme = value;
|
||||||
],
|
}
|
||||||
),
|
|
||||||
const Divider(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'More',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.primary),
|
|
||||||
),
|
|
||||||
DropdownButtonFormField(
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText:
|
|
||||||
'Background Update Checking Interval'),
|
|
||||||
value: settingsProvider.updateInterval,
|
|
||||||
items: const [
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 15,
|
|
||||||
child: Text('15 Minutes'),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 30,
|
|
||||||
child: Text('30 Minutes'),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 60,
|
|
||||||
child: Text('1 Hour'),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 360,
|
|
||||||
child: Text('6 Hours'),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 720,
|
|
||||||
child: Text('12 Hours'),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 1440,
|
|
||||||
child: Text('1 Day'),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 0,
|
|
||||||
child: Text('Never - Manual Only'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
settingsProvider.updateInterval = value;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
const Spacer(),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
TextButton.icon(
|
|
||||||
style: ButtonStyle(
|
|
||||||
foregroundColor:
|
|
||||||
MaterialStateProperty.resolveWith<Color>(
|
|
||||||
(Set<MaterialState> states) {
|
|
||||||
return Colors.grey;
|
|
||||||
}),
|
}),
|
||||||
),
|
const SizedBox(
|
||||||
onPressed: () {
|
height: 16,
|
||||||
launchUrlString(settingsProvider.sourceUrl,
|
),
|
||||||
mode: LaunchMode.externalApplication);
|
DropdownButtonFormField(
|
||||||
},
|
decoration:
|
||||||
icon: const Icon(Icons.code),
|
const InputDecoration(labelText: 'Colour'),
|
||||||
label: Text(
|
value: settingsProvider.colour,
|
||||||
'Source',
|
items: const [
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
DropdownMenuItem(
|
||||||
),
|
value: ColourSettings.basic,
|
||||||
)
|
child: Text('Obtainium'),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: ColourSettings.materialYou,
|
||||||
|
child: Text('Material You'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
settingsProvider.colour = value;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: DropdownButtonFormField(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'App Sort By'),
|
||||||
|
value: settingsProvider.sortColumn,
|
||||||
|
items: const [
|
||||||
|
DropdownMenuItem(
|
||||||
|
value:
|
||||||
|
SortColumnSettings.authorName,
|
||||||
|
child: Text('Author/Name'),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value:
|
||||||
|
SortColumnSettings.nameAuthor,
|
||||||
|
child: Text('Name/Author'),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: SortColumnSettings.added,
|
||||||
|
child: Text('As Added'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
settingsProvider.sortColumn = value;
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: DropdownButtonFormField(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'App Sort Order'),
|
||||||
|
value: settingsProvider.sortOrder,
|
||||||
|
items: const [
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: SortOrderSettings.ascending,
|
||||||
|
child: Text('Ascending'),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: SortOrderSettings.descending,
|
||||||
|
child: Text('Descending'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
settingsProvider.sortOrder = value;
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text('Show Source Webpage in App View'),
|
||||||
|
Switch(
|
||||||
|
value: settingsProvider.showAppWebpage,
|
||||||
|
onChanged: (value) {
|
||||||
|
settingsProvider.showAppWebpage = value;
|
||||||
|
})
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'More',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.primary),
|
||||||
|
),
|
||||||
|
DropdownButtonFormField(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText:
|
||||||
|
'Background Update Checking Interval'),
|
||||||
|
value: settingsProvider.updateInterval,
|
||||||
|
items: const [
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 15,
|
||||||
|
child: Text('15 Minutes'),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 30,
|
||||||
|
child: Text('30 Minutes'),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 60,
|
||||||
|
child: Text('1 Hour'),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 360,
|
||||||
|
child: Text('6 Hours'),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 720,
|
||||||
|
child: Text('12 Hours'),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 1440,
|
||||||
|
child: Text('1 Day'),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 0,
|
||||||
|
child: Text('Never - Manual Only'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
settingsProvider.updateInterval = value;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
const Spacer(),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
TextButton.icon(
|
||||||
|
style: ButtonStyle(
|
||||||
|
foregroundColor:
|
||||||
|
MaterialStateProperty.resolveWith<
|
||||||
|
Color>((Set<MaterialState> states) {
|
||||||
|
return Colors.grey;
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
launchUrlString(settingsProvider.sourceUrl,
|
||||||
|
mode: LaunchMode.externalApplication);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.code),
|
||||||
|
label: Text(
|
||||||
|
'Source',
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
)))
|
||||||
],
|
]));
|
||||||
)))
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 0.2.2+13 # When changing this, update the tag in main() accordingly
|
version: 0.2.4+15 # When changing this, update the tag in main() accordingly
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.19.0-79.0.dev <3.0.0'
|
sdk: '>=2.19.0-79.0.dev <3.0.0'
|
||||||
|
Reference in New Issue
Block a user