mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-08-11 01:20:16 +02:00
Improve release asset download UI
This commit is contained in:
@@ -155,7 +155,8 @@ class AddAppPageState extends State<AddAppPage> {
|
|||||||
// Only download the APK here if you need to for the package ID
|
// Only download the APK here if you need to for the package ID
|
||||||
if (isTempId(app) && app.additionalSettings['trackOnly'] != true) {
|
if (isTempId(app) && app.additionalSettings['trackOnly'] != true) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
var apkUrl = await appsProvider.confirmApkUrl(app, context);
|
var apkUrl =
|
||||||
|
await appsProvider.confirmAppFileUrl(app, context, false);
|
||||||
if (apkUrl == null) {
|
if (apkUrl == null) {
|
||||||
throw ObtainiumError(tr('cancelled'));
|
throw ObtainiumError(tr('cancelled'));
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
import 'package:animations/animations.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:obtainium/components/generated_form.dart';
|
|
||||||
import 'package:obtainium/components/generated_form_modal.dart';
|
import 'package:obtainium/components/generated_form_modal.dart';
|
||||||
import 'package:obtainium/custom_errors.dart';
|
import 'package:obtainium/custom_errors.dart';
|
||||||
import 'package:obtainium/main.dart';
|
import 'package:obtainium/main.dart';
|
||||||
@@ -167,46 +165,19 @@ class _AppPageState extends State<AppPage> {
|
|||||||
onTap: app?.app == null || updating
|
onTap: app?.app == null || updating
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
var allAssetUrls = [
|
var fileUrl = await appsProvider.confirmAppFileUrl(
|
||||||
...app!.app.apkUrls,
|
app!.app, context, true);
|
||||||
...app.app.otherAssetUrls
|
if (fileUrl != null) {
|
||||||
].map((e) => MapEntry(e.value, e.key)).toList();
|
|
||||||
var values = await showModal(
|
|
||||||
context: globalNavigatorKey.currentContext ?? context,
|
|
||||||
builder: (BuildContext ctx) {
|
|
||||||
return GeneratedFormModal(
|
|
||||||
title:
|
|
||||||
tr('downloadX', args: [tr('releaseAsset')]),
|
|
||||||
initValid: true,
|
|
||||||
items: [
|
|
||||||
[
|
|
||||||
GeneratedFormDropdown(
|
|
||||||
'assetToDownload', allAssetUrls,
|
|
||||||
defaultValue: allAssetUrls[0].key,
|
|
||||||
label: tr('selectX', args: [
|
|
||||||
tr('releaseAsset').toLowerCase()
|
|
||||||
]))
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (values != null) {
|
|
||||||
var downloadUrl = values['assetToDownload'] as String;
|
|
||||||
var fileName = allAssetUrls
|
|
||||||
.where((e) => e.key == downloadUrl)
|
|
||||||
.first
|
|
||||||
.value;
|
|
||||||
NotificationsProvider notificationsProvider =
|
NotificationsProvider notificationsProvider =
|
||||||
(globalNavigatorKey.currentContext ?? context)
|
(globalNavigatorKey.currentContext ?? context)
|
||||||
.read<NotificationsProvider>();
|
.read<NotificationsProvider>();
|
||||||
try {
|
try {
|
||||||
showMessage(
|
showMessage(
|
||||||
'${tr('downloadingX', args: [fileName])}...',
|
'${tr('downloadingX', args: [fileUrl.key])}...',
|
||||||
globalNavigatorKey.currentContext ?? context);
|
globalNavigatorKey.currentContext ?? context);
|
||||||
await downloadFile(
|
await downloadFile(
|
||||||
downloadUrl,
|
fileUrl.value,
|
||||||
fileName
|
fileUrl.key
|
||||||
.split('.')
|
.split('.')
|
||||||
.reversed
|
.reversed
|
||||||
.toList()
|
.toList()
|
||||||
@@ -214,21 +185,21 @@ class _AppPageState extends State<AppPage> {
|
|||||||
.reversed
|
.reversed
|
||||||
.join('.'), (double? progress) {
|
.join('.'), (double? progress) {
|
||||||
notificationsProvider.notify(DownloadNotification(
|
notificationsProvider.notify(DownloadNotification(
|
||||||
fileName, progress?.ceil() ?? 0));
|
fileUrl.key, progress?.ceil() ?? 0));
|
||||||
}, '/storage/emulated/0/Download',
|
}, '/storage/emulated/0/Download',
|
||||||
headers: await source?.getRequestHeaders(
|
headers: await source?.getRequestHeaders(
|
||||||
app.app.additionalSettings,
|
app.app.additionalSettings,
|
||||||
forAPKDownload: fileName.endsWith('.apk')
|
forAPKDownload: fileUrl.key.endsWith('.apk')
|
||||||
? true
|
? true
|
||||||
: false));
|
: false));
|
||||||
notificationsProvider.notify(
|
notificationsProvider.notify(DownloadedNotification(
|
||||||
DownloadedNotification(fileName, downloadUrl));
|
fileUrl.key, fileUrl.value));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showError(
|
showError(
|
||||||
e, globalNavigatorKey.currentContext ?? context);
|
e, globalNavigatorKey.currentContext ?? context);
|
||||||
} finally {
|
} finally {
|
||||||
notificationsProvider
|
notificationsProvider
|
||||||
.cancel(DownloadNotification(fileName, 0).id);
|
.cancel(DownloadNotification(fileUrl.key, 0).id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -705,23 +705,28 @@ class AppsProvider with ChangeNotifier {
|
|||||||
await intent.launch();
|
await intent.launch();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<MapEntry<String, String>?> confirmApkUrl(
|
Future<MapEntry<String, String>?> confirmAppFileUrl(
|
||||||
App app, BuildContext? context) async {
|
App app, BuildContext? context, bool pickAnyAsset) async {
|
||||||
|
var urlsToSelectFrom = app.apkUrls;
|
||||||
|
if (pickAnyAsset) {
|
||||||
|
urlsToSelectFrom = [...urlsToSelectFrom, ...app.otherAssetUrls];
|
||||||
|
}
|
||||||
// If the App has more than one APK, the user should pick one (if context provided)
|
// If the App has more than one APK, the user should pick one (if context provided)
|
||||||
MapEntry<String, String>? apkUrl =
|
MapEntry<String, String>? appFileUrl = urlsToSelectFrom[
|
||||||
app.apkUrls[app.preferredApkIndex >= 0 ? app.preferredApkIndex : 0];
|
app.preferredApkIndex >= 0 ? app.preferredApkIndex : 0];
|
||||||
// get device supported architecture
|
// get device supported architecture
|
||||||
List<String> archs = (await DeviceInfoPlugin().androidInfo).supportedAbis;
|
List<String> archs = (await DeviceInfoPlugin().androidInfo).supportedAbis;
|
||||||
|
|
||||||
if (app.apkUrls.length > 1 && context != null) {
|
if (urlsToSelectFrom.length > 1 && context != null) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
apkUrl = await showDialog(
|
appFileUrl = await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext ctx) {
|
builder: (BuildContext ctx) {
|
||||||
return APKPicker(
|
return AppFilePicker(
|
||||||
app: app,
|
app: app,
|
||||||
initVal: apkUrl,
|
initVal: appFileUrl,
|
||||||
archs: archs,
|
archs: archs,
|
||||||
|
pickAnyAsset: pickAnyAsset,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -731,8 +736,8 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the picked APK comes from an origin different from the source, get user confirmation (if context provided)
|
// If the picked APK comes from an origin different from the source, get user confirmation (if context provided)
|
||||||
if (apkUrl != null &&
|
if (appFileUrl != null &&
|
||||||
getHost(apkUrl.value) != getHost(app.url) &&
|
getHost(appFileUrl.value) != getHost(app.url) &&
|
||||||
context != null) {
|
context != null) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
if (!(settingsProvider.hideAPKOriginWarning) &&
|
if (!(settingsProvider.hideAPKOriginWarning) &&
|
||||||
@@ -741,13 +746,13 @@ class AppsProvider with ChangeNotifier {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext ctx) {
|
builder: (BuildContext ctx) {
|
||||||
return APKOriginWarningDialog(
|
return APKOriginWarningDialog(
|
||||||
sourceUrl: app.url, apkUrl: apkUrl!.value);
|
sourceUrl: app.url, apkUrl: appFileUrl!.value);
|
||||||
}) !=
|
}) !=
|
||||||
true) {
|
true) {
|
||||||
apkUrl = null;
|
appFileUrl = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return apkUrl;
|
return appFileUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given a list of AppIds, uses stored info about the apps to download APKs and install them
|
// Given a list of AppIds, uses stored info about the apps to download APKs and install them
|
||||||
@@ -774,7 +779,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
var trackOnly = apps[id]!.app.additionalSettings['trackOnly'] == true;
|
var trackOnly = apps[id]!.app.additionalSettings['trackOnly'] == true;
|
||||||
if (!trackOnly) {
|
if (!trackOnly) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
apkUrl = await confirmApkUrl(apps[id]!.app, context);
|
apkUrl = await confirmAppFileUrl(apps[id]!.app, context, false);
|
||||||
}
|
}
|
||||||
if (apkUrl != null) {
|
if (apkUrl != null) {
|
||||||
int urlInd = apps[id]!
|
int urlInd = apps[id]!
|
||||||
@@ -1482,38 +1487,49 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class APKPicker extends StatefulWidget {
|
class AppFilePicker extends StatefulWidget {
|
||||||
const APKPicker({super.key, required this.app, this.initVal, this.archs});
|
const AppFilePicker(
|
||||||
|
{super.key,
|
||||||
|
required this.app,
|
||||||
|
this.initVal,
|
||||||
|
this.archs,
|
||||||
|
this.pickAnyAsset = false});
|
||||||
|
|
||||||
final App app;
|
final App app;
|
||||||
final MapEntry<String, String>? initVal;
|
final MapEntry<String, String>? initVal;
|
||||||
final List<String>? archs;
|
final List<String>? archs;
|
||||||
|
final bool pickAnyAsset;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<APKPicker> createState() => _APKPickerState();
|
State<AppFilePicker> createState() => _AppFilePickerState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _APKPickerState extends State<APKPicker> {
|
class _AppFilePickerState extends State<AppFilePicker> {
|
||||||
MapEntry<String, String>? apkUrl;
|
MapEntry<String, String>? fileUrl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
apkUrl ??= widget.initVal;
|
fileUrl ??= widget.initVal;
|
||||||
|
var urlsToSelectFrom = widget.app.apkUrls;
|
||||||
|
if (widget.pickAnyAsset) {
|
||||||
|
urlsToSelectFrom = [...urlsToSelectFrom, ...widget.app.otherAssetUrls];
|
||||||
|
}
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
scrollable: true,
|
scrollable: true,
|
||||||
title: Text(tr('pickAnAPK')),
|
title: Text(widget.pickAnyAsset
|
||||||
|
? tr('selectX', args: [tr('releaseAsset').toLowerCase()])
|
||||||
|
: tr('pickAnAPK')),
|
||||||
content: Column(children: [
|
content: Column(children: [
|
||||||
Text(tr('appHasMoreThanOnePackage', args: [widget.app.finalName])),
|
Text(tr('appHasMoreThanOnePackage', args: [widget.app.finalName])),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
...widget.app.apkUrls.map(
|
...urlsToSelectFrom.map(
|
||||||
(u) => RadioListTile<String>(
|
(u) => RadioListTile<String>(
|
||||||
title: Text(u.key),
|
title: Text(u.key),
|
||||||
value: u.value,
|
value: u.value,
|
||||||
groupValue: apkUrl!.value,
|
groupValue: fileUrl!.value,
|
||||||
onChanged: (String? val) {
|
onChanged: (String? val) {
|
||||||
setState(() {
|
setState(() {
|
||||||
apkUrl =
|
fileUrl = urlsToSelectFrom.where((e) => e.value == val).first;
|
||||||
widget.app.apkUrls.where((e) => e.value == val).first;
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -1540,7 +1556,7 @@ class _APKPickerState extends State<APKPicker> {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
HapticFeedback.selectionClick();
|
HapticFeedback.selectionClick();
|
||||||
Navigator.of(context).pop(apkUrl);
|
Navigator.of(context).pop(fileUrl);
|
||||||
},
|
},
|
||||||
child: Text(tr('continue')))
|
child: Text(tr('continue')))
|
||||||
],
|
],
|
||||||
|
Reference in New Issue
Block a user