diff --git a/lib/app_sources/html.dart b/lib/app_sources/html.dart
index d9ec173..c6ec9e2 100644
--- a/lib/app_sources/html.dart
+++ b/lib/app_sources/html.dart
@@ -89,6 +89,13 @@ class HTML extends AppSource {
overrideEligible = true;
}
+ @override
+ // TODO: implement requestHeaders choice, hardcoded for now
+ Map? get requestHeaders => {
+ "User-Agent":
+ "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0"
+ };
+
@override
String sourceSpecificStandardizeURL(String url) {
return url;
diff --git a/lib/main.dart b/lib/main.dart
index b9044c4..900cd41 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -21,7 +21,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
// ignore: implementation_imports
import 'package:easy_localization/src/localization.dart';
-const String currentVersion = '0.13.1';
+const String currentVersion = '0.13.2';
const String currentReleaseTag =
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
diff --git a/lib/pages/add_app.dart b/lib/pages/add_app.dart
index bdb18e4..ac85b10 100644
--- a/lib/pages/add_app.dart
+++ b/lib/pages/add_app.dart
@@ -283,6 +283,9 @@ class _AddAppPageState extends State {
}
si++;
}
+ if (res.isEmpty) {
+ throw ObtainiumError(tr('noResults'));
+ }
List? selectedUrls = res.isEmpty
? []
// ignore: use_build_context_synchronously
@@ -377,13 +380,15 @@ class _AddAppPageState extends State {
const SizedBox(
width: 16,
),
- ElevatedButton(
- onPressed: searchQuery.isEmpty || doingSomething
- ? null
- : () {
- runSearch();
- },
- child: Text(tr('search')))
+ searching
+ ? const CircularProgressIndicator()
+ : ElevatedButton(
+ onPressed: searchQuery.isEmpty || doingSomething
+ ? null
+ : () {
+ runSearch();
+ },
+ child: Text(tr('search')))
],
);
diff --git a/lib/pages/app.dart b/lib/pages/app.dart
index 8f851e5..b7585c8 100644
--- a/lib/pages/app.dart
+++ b/lib/pages/app.dart
@@ -444,7 +444,9 @@ class _AppPageState extends State {
Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 0),
child: LinearProgressIndicator(
- value: app!.downloadProgress! / 100))
+ value: app!.downloadProgress! >= 0
+ ? app.downloadProgress! / 100
+ : null))
],
));
diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart
index 5fcee6a..37fa60c 100644
--- a/lib/pages/apps.dart
+++ b/lib/pages/apps.dart
@@ -542,8 +542,12 @@ class AppsPageState extends State {
? SizedBox(
width: 110,
child: Text(tr('percentProgress', args: [
- listedApps[index].downloadProgress?.toInt().toString() ??
- '100'
+ listedApps[index].downloadProgress! >= 0
+ ? listedApps[index]
+ .downloadProgress!
+ .toInt()
+ .toString()
+ : tr('pleaseWait')
])))
: trailingRow,
onTap: () {
diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart
index 3e6f5ea..d15c5e3 100644
--- a/lib/providers/apps_provider.dart
+++ b/lib/providers/apps_provider.dart
@@ -122,25 +122,34 @@ class AppsProvider with ChangeNotifier {
// Load Apps into memory (in background, this is done later instead of in the constructor)
await loadApps();
// Delete any partial APKs
+ var cutoff = DateTime.now().subtract(const Duration(days: 7));
(await getExternalCacheDirectories())
?.first
.listSync()
- .where((element) => element.path.endsWith('.apk.part'))
+ .where((element) =>
+ element.path.endsWith('.part') ||
+ element.statSync().modified.isBefore(cutoff))
.forEach((partialApk) {
partialApk.delete();
});
}();
}
- downloadFile(String url, String fileName, Function? onProgress,
+ Future downloadFile(
+ String url, String fileNameNoExt, Function? onProgress,
{bool useExisting = true, Map? headers}) async {
var destDir = (await getExternalCacheDirectories())!.first.path;
var req = Request('GET', Uri.parse(url));
if (headers != null) {
req.headers.addAll(headers);
}
- StreamedResponse response = await Client().send(req);
- File downloadedFile = File('$destDir/$fileName');
+ var client = Client();
+ StreamedResponse response = await client.send(req);
+ var ext = response.headers['content-disposition']!.split('.').last;
+ if (ext.endsWith('"') || ext.endsWith("other")) {
+ ext = ext.substring(0, ext.length - 1);
+ }
+ File downloadedFile = File('$destDir/$fileNameNoExt.$ext');
if (!(downloadedFile.existsSync() && useExisting)) {
File tempDownloadedFile = File('${downloadedFile.path}.part');
if (tempDownloadedFile.existsSync()) {
@@ -168,12 +177,14 @@ class AppsProvider with ChangeNotifier {
throw response.reasonPhrase ?? tr('unexpectedError');
}
tempDownloadedFile.renameSync(downloadedFile.path);
+ } else {
+ client.close();
}
return downloadedFile;
}
- handleAPKIDChange(App app, PackageArchiveInfo newInfo, File downloadedFile,
- String downloadUrl) async {
+ Future handleAPKIDChange(App app, PackageArchiveInfo newInfo,
+ File downloadedFile, String downloadUrl) async {
// If the APK package ID is different from the App ID, it is either new (using a placeholder ID) or the ID has changed
// The former case should be handled (give the App its real ID), the latter is a security issue
if (app.id != newInfo.packageName) {
@@ -184,12 +195,13 @@ class AppsProvider with ChangeNotifier {
var originalAppId = app.id;
app.id = newInfo.packageName;
downloadedFile = downloadedFile.renameSync(
- '${downloadedFile.parent.path}/${app.id}-${downloadUrl.hashCode}.apk');
+ '${downloadedFile.parent.path}/${app.id}-${downloadUrl.hashCode}.${downloadedFile.path.split('.').last}');
if (apps[originalAppId] != null) {
await removeApps([originalAppId]);
await saveApps([app], onlyIfExists: !isTempId);
}
}
+ return downloadedFile;
}
Future