Compare commits

...

22 Commits

Author SHA1 Message Date
8ba0a0a776 Merge pull request #852 from ImranR98/dev
Fix Aptoide App ID inferral error (#851)
2023-09-04 03:57:56 -04:00
73ed0cea88 Increment version 2023-09-04 03:56:41 -04:00
58a378d212 Fix Aptoide App ID inferral error (#851) 2023-09-04 03:55:13 -04:00
553307ba70 Merge pull request #850 from ImranR98/dev
Auto Select Highest VersionCode APK (F-Droid) (#846)
2023-09-03 16:12:24 -04:00
78f73a9049 Auto Select Highest VersionCode APK (F-Droid) (#846) 2023-09-03 16:11:17 -04:00
abc69e7a0e Merge pull request #849 from ImranR98/dev
Bugfix in HTML Source (#848)
2023-09-03 15:37:43 -04:00
503914dbce Bugfix in HTML Source (#848) 2023-09-03 15:37:26 -04:00
69680f8680 Merge pull request #844 from gidano/main
Update hu.json
2023-09-03 00:15:41 -04:00
1a616aacb7 Merge pull request #847 from ImranR98/dev
Add Aptoide as a Source (#843)
2023-09-03 00:15:01 -04:00
52d19f267e Upgrade packages, increment version 2023-09-03 00:14:23 -04:00
75a8dc9ee4 Add Aptoide as a Source (#843) 2023-09-02 21:28:30 -04:00
8c63920ef6 Update hu.json 2023-09-02 15:41:10 +02:00
dda5619cdc Update issue templates 2023-09-01 11:25:53 -04:00
9cc25fd931 Update issue templates 2023-09-01 11:18:42 -04:00
f5a9eb4295 Update issue templates 2023-09-01 11:13:04 -04:00
ebe256e482 Update issue templates 2023-09-01 11:05:49 -04:00
e215585a64 Merge pull request #840 from ImranR98/dev
Bugfix: BG update fail leads to infinite retries (#838), Group update notifications into one (#829), Temporarily exclude Obtainium from BG updates (#836)
2023-08-30 22:36:31 -04:00
8b01fc03ec Increment version 2023-08-30 22:35:11 -04:00
9af2c8370d Bugfix: BG update fail leads to infinite retries (#838) 2023-08-30 22:34:12 -04:00
01f9003b8d Group update notifications into one (#829) 2023-08-30 22:07:30 -04:00
73a3c7eb71 Temporarily exclude Obtainium from BG updates (#836) 2023-08-30 21:41:26 -04:00
370ec1432e Fix BG update OS requirement 2023-08-30 21:40:05 -04:00
25 changed files with 324 additions and 100 deletions

32
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,32 @@
---
name: Bug report
about: Something isn't working right.
title: ''
labels: bug, To Check
assignees: ''
---
**Prerequisites**
Please ensure your request is not part of an existing issue.
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Tap on '....'
3. Scroll down to '....'
4. See error
**Screenshots and Logs**
If applicable, add screenshots, logs, and any other artifacts (like some/all files under `/Android/data/dev.imranr.obtainium/`) that you think may help troubleshoot the issue.
**Please complete the following information:**
- Device: [e.g. Pixel 7]
- OS: [e.g. GrapheneOS]
- Obtainium Version [e.g. 0.14.6-beta]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,29 @@
---
name: Feature request
about: Suggest a new Source, setting, or other feature.
title: ''
labels: enhancement, To Check
assignees: ''
---
**Prerequisites**
Please ensure your request is not part of an existing issue.
**Describe the feature**
A clear and concise description of what you want to happen.
For new Sources, it's preferable (not required) if you suggest how the following details can be extracted from the Source in a reliable way (like an API or through web scraping):
- The App version (or any release-specific identifier - a "pseudo-version") for the latest release
- One or more APK URL(s) for the latest release
- Above details for previous releases (optional)
Note that the Web scraper cannot deal with JavaScript-enabled content.
**Describe alternatives you've considered (if applicable)**
A clear and concise description of any alternative solutions or features you've considered.
Note that app-specific Sources are less likely to be added. In those cases, see if the HTML Source will work for you (if not, see if a generally-applicable enhancement to the HTML Source would work, and suggest that instead).
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -16,6 +16,7 @@ Currently supported App sources:
- [Signal](https://signal.org/)
- [SourceForge](https://sourceforge.net/)
- [SourceHut](https://git.sr.ht/)
- [Aptoide](https://aptoide.com/)
- [APKMirror](https://apkmirror.com/) (Track-Only)
- [APKPure](https://apkpure.com/)
- [Huawei AppGallery](https://appgallery.huawei.com/)

View File

@ -252,6 +252,7 @@
"intermediateLinkNotFound": "Link intermediário não encontrado",
"exemptFromBackgroundUpdates": "Isento de atualizações em segundo plano (se ativadas)",
"bgUpdatesOnWiFiOnly": "Desative atualizações em segundo plano quando não estiver em WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "Remover App?",
"other": "Remover Apps?"

View File

@ -249,6 +249,7 @@
"verifyLatestTag": "Verify the 'latest' tag",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "Želite li ukloniti aplikaciju?",
"other": "Želite li ukloniti aplikacije?"

View File

@ -249,6 +249,7 @@
"verifyLatestTag": "Überprüfe das 'latest' Tag",
"exemptFromBackgroundUpdates": "Ausschluss von Hintergrundaktualisierungen (falls aktiviert)",
"bgUpdatesOnWiFiOnly": "Hintergrundaktualisierungen deaktivieren, wenn kein WLAN vorhanden ist",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "App entfernen?",
"other": "Apps entfernen?"

View File

@ -252,6 +252,7 @@
"intermediateLinkNotFound": "Intermediate link not found",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "Remove App?",
"other": "Remove Apps?"

View File

@ -249,6 +249,7 @@
"verifyLatestTag": "Verify the 'latest' tag",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "¿Eliminar Aplicación?",
"other": "¿Eliminar Aplicaciones?"

View File

@ -249,6 +249,7 @@
"verifyLatestTag": "Verify the 'latest' tag",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "برنامه حذف شود؟",
"other": "برنامه ها حذف شوند؟"

View File

@ -249,6 +249,7 @@
"verifyLatestTag": "Verify the 'latest' tag",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "Supprimer l'application ?",
"other": "Supprimer les applications ?"

View File

@ -87,7 +87,7 @@
"author": "Szerző",
"upToDateApps": "Naprakész appok",
"nonInstalledApps": "Nem telepített appok",
"importExport": "Importálás/Exportálás",
"importExport": "Import/Export",
"settings": "Beállítások",
"exportedTo": "Exportálva ide {}",
"obtainiumExport": "Obtainium Adat Exportálás",
@ -137,7 +137,7 @@
"share": "Megosztás",
"appNotFound": "App nem található",
"obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "Válasszon egy APK-ot",
"pickAnAPK": "Válasszon egy APK-t",
"appHasMoreThanOnePackage": "A(z) {} egynél több csomaggal rendelkezik:",
"deviceSupportsXArch": "Eszköze támogatja a {} CPU architektúrát.",
"deviceSupportsFollowingArchs": "Az eszköze a következő CPU architektúrákat támogatja:",
@ -204,7 +204,7 @@
"copiedToClipboard": "Másolva a vágólapra",
"storagePermissionDenied": "Tárhely engedély megtagadva",
"selectedCategorizeWarning": "Ez felváltja a kiválasztott alkalmazások meglévő kategória-beállításait.",
"filterAPKsByRegEx": "Az APK-ok szűrése reguláris kifejezéssel",
"filterAPKsByRegEx": "Az APK-k szűrése reguláris kifejezéssel",
"removeFromObtainium": "Eltávolítás az Obtainiumból",
"uninstallFromDevice": "Eltávolítás a készülékről",
"onlyWorksWithNonVersionDetectApps": "Csak azoknál az alkalmazásoknál működik, amelyeknél a verzióérzékelés le van tiltva.",
@ -239,15 +239,16 @@
"gitlabSourceNote": "Előfordulhat, hogy a GitLab APK kibontása nem működik API-kulcs nélkül.",
"sortByFileNamesNotLinks": "Fájlnevek szerinti elrendezés teljes linkek helyett",
"filterReleaseNotesByRegEx": "Kiadási megjegyzések szűrése reguláris kifejezéssel",
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
"appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"customLinkFilterRegex": "Egyéni APK hivatkozásszűrő reguláris kifejezéssel (Alapérték '.apk$')",
"appsPossiblyUpdated": "App frissítési kísérlet",
"appsPossiblyUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy vagy több alkalmazás frissítése lehetséges a háttérben",
"xWasPossiblyUpdatedToY": "{} frissítve lehet erre {}.",
"backgroundUpdateReqsExplanation": "Előfordulhat, hogy nem minden appnál lehetséges a háttérbeli frissítés.",
"backgroundUpdateLimitsExplanation": "A háttérben történő telepítés sikeressége csak az Obtainium megnyitásakor állapítható meg.",
"verifyLatestTag": "Ellenőrizze a „legújabb” címkét",
"exemptFromBackgroundUpdates": "Mentes a háttérben történő frissítések alól (ha engedélyezett)",
"bgUpdatesOnWiFiOnly": "Tiltsa le a háttérben frissítéseket, ha nincs Wi-Fi-n",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "Eltávolítja az alkalmazást?",
"other": "Eltávolítja az alkalmazást?"
@ -297,7 +298,7 @@
"other": "{} és {} további alkalmazás frissítve."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated."
"one": "{} és 1 további alkalmazás is frissült.",
"other": "{} és {} további alkalmazás is frissült."
}
}

View File

@ -249,6 +249,7 @@
"verifyLatestTag": "Verify the 'latest' tag",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "Rimuovere l'app?",
"other": "Rimuovere le app?"

View File

@ -250,6 +250,7 @@
"verifyLatestTag": "'latest'タグを確認する",
"exemptFromBackgroundUpdates": "バックグラウンドアップデートを行わない (有効な場合)",
"bgUpdatesOnWiFiOnly": "WiFiを使用していない場合バックグラウンドアップデートを無効にする",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "アプリを削除しますか?",
"other": "アプリを削除しますか?"

View File

@ -255,6 +255,7 @@
"verifyLatestTag": "Zweryfikuj najnowszy tag",
"exemptFromBackgroundUpdates": "Wyklucz z uaktualnień w tle (jeśli są włączone)",
"bgUpdatesOnWiFiOnly": "Wyłącz aktualizacje w tle, gdy nie ma połączenia z Wi-Fi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "Usunąć aplikację?",
"few": "Usunąć aplikacje?",

View File

@ -249,6 +249,7 @@
"verifyLatestTag": "Verify the 'latest' tag",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "Удалить приложение?",
"other": "Удалить приложения?"

View File

@ -250,6 +250,7 @@
"verifyLatestTag": "验证“Latest”标签",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"removeAppQuestion": {
"one": "是否删除应用?",
"other": "是否删除应用?"

View File

@ -0,0 +1,108 @@
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
class Aptoide extends AppSource {
Aptoide() {
host = 'aptoide.com';
name = tr('Aptoide');
allowSubDomains = true;
}
@override
String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://([^\\.]+\\.){2,}$host');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) {
throw InvalidURLError(name);
}
return url.substring(0, match.end);
}
@override
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async {
return (await getAppDetailsJSON(standardUrl))['package'];
}
Future<Map<String, dynamic>> getAppDetailsJSON(String standardUrl) async {
var res = await sourceRequest(standardUrl);
if (res.statusCode != 200) {
throw getObtainiumHttpError(res);
}
var idMatch = RegExp('"app":{"id":[0-9]+').firstMatch(res.body);
String? id;
if (idMatch != null) {
id = res.body.substring(idMatch.start + 12, idMatch.end);
} else {
throw NoReleasesError();
}
var res2 =
await sourceRequest('https://ws2.aptoide.com/api/7/getApp/app_id/$id');
if (res2.statusCode != 200) {
throw getObtainiumHttpError(res);
}
return jsonDecode(res2.body)?['nodes']?['meta']?['data'];
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
var appDetails = await getAppDetailsJSON(standardUrl);
String appName = appDetails['name'] ?? tr('app');
String author = appDetails['developer']?['name'] ?? name;
String? dateStr = appDetails['updated'];
String? version = appDetails['file']?['vername'];
String? apkUrl = appDetails['file']?['path'];
if (version == null) {
throw NoVersionError();
}
if (apkUrl == null) {
throw NoAPKError();
}
DateTime? relDate;
if (dateStr != null) {
relDate = DateTime.parse(dateStr);
}
return APKDetails(
version, getApkUrlsFromUrls([apkUrl]), AppNames(author, appName),
releaseDate: relDate);
}
@override
Future<Map<String, List<String>>> search(String query,
{Map<String, dynamic> querySettings = const {}}) async {
Response res = await sourceRequest(
'https://search.$host/?q=${Uri.encodeQueryComponent(query)}');
if (res.statusCode == 200) {
Map<String, List<String>> urlsWithDescriptions = {};
parse(res.body).querySelectorAll('.package-header').forEach((e) {
String? url = e.attributes['href'];
if (url != null) {
try {
standardizeUrl(url);
} catch (e) {
url = null;
}
}
if (url != null) {
urlsWithDescriptions[url] = [
e.querySelector('.package-name')?.text.trim() ?? '',
e.querySelector('.package-summary')?.text.trim() ??
tr('noDescription')
];
}
});
return urlsWithDescriptions;
} else {
throw getObtainiumHttpError(res);
}
}
}

View File

@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
@ -11,6 +12,12 @@ class FDroid extends AppSource {
host = 'f-droid.org';
name = tr('fdroid');
canSearch = true;
additionalSourceAppSpecificSettingFormItems = [
[
GeneratedFormSwitch('autoSelectHighestVersionCode',
label: tr('autoSelectHighestVersionCode'))
]
];
}
@override
@ -37,7 +44,8 @@ class FDroid extends AppSource {
}
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
Response res, String apkUrlPrefix, String standardUrl) {
Response res, String apkUrlPrefix, String standardUrl,
{bool autoSelectHighestVersionCode = false}) {
if (res.statusCode == 200) {
List<dynamic> releases = jsonDecode(res.body)['packages'] ?? [];
if (releases.isEmpty) {
@ -47,8 +55,12 @@ class FDroid extends AppSource {
if (latestVersion == null) {
throw NoVersionError();
}
List<String> apkUrls = releases
.where((element) => element['versionName'] == latestVersion)
Iterable<dynamic> latestReleases =
releases.where((element) => element['versionName'] == latestVersion);
if (latestReleases.length > 1 && autoSelectHighestVersionCode) {
latestReleases = [latestReleases.first];
}
List<String> apkUrls = latestReleases
.map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
.toList();
return APKDetails(latestVersion, getApkUrlsFromUrls(apkUrls),
@ -68,7 +80,9 @@ class FDroid extends AppSource {
return getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest('https://$host/api/v1/packages/$appId'),
'https://$host/repo/$appId',
standardUrl);
standardUrl,
autoSelectHighestVersionCode:
additionalSettings['autoSelectHighestVersionCode'] == true);
}
@override

View File

@ -18,7 +18,7 @@ String ensureAbsoluteUrl(String ambiguousUrl, Uri referenceAbsoluteUrl) {
.toList();
if (ambiguousUrl.startsWith('/') || currPathSegments.isEmpty) {
return '${referenceAbsoluteUrl.origin}/$ambiguousUrl';
} else if (ambiguousUrl.split('/').length == 1) {
} else if (ambiguousUrl.split('/').where((e) => e.isNotEmpty).length == 1) {
return '${referenceAbsoluteUrl.origin}/${currPathSegments.join('/')}/$ambiguousUrl';
} else {
return '${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - 1).join('/')}/$ambiguousUrl';

View File

@ -3,8 +3,13 @@ import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
class IzzyOnDroid extends AppSource {
late FDroid fd;
IzzyOnDroid() {
host = 'android.izzysoft.de';
fd = FDroid();
additionalSourceAppSpecificSettingFormItems =
fd.additionalSourceAppSpecificSettingFormItems;
}
@override
@ -20,7 +25,7 @@ class IzzyOnDroid extends AppSource {
@override
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async {
return FDroid().tryInferringAppId(standardUrl);
return fd.tryInferringAppId(standardUrl);
}
@override
@ -29,10 +34,12 @@ class IzzyOnDroid extends AppSource {
Map<String, dynamic> additionalSettings,
) async {
String? appId = await tryInferringAppId(standardUrl);
return FDroid().getAPKUrlsFromFDroidPackagesAPIResponse(
return fd.getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest(
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),
'https://android.izzysoft.de/frepo/$appId',
standardUrl);
standardUrl,
autoSelectHighestVersionCode:
additionalSettings['autoSelectHighestVersionCode'] == true);
}
}

View File

@ -19,7 +19,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
// ignore: implementation_imports
import 'package:easy_localization/src/localization.dart';
const String currentVersion = '0.14.5';
const String currentVersion = '0.14.10';
const String currentReleaseTag =
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES

View File

@ -4,6 +4,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
import 'package:android_intent_plus/flag.dart';
@ -116,16 +117,19 @@ moveStrToEnd(List<String> arr, String str, {String? strB}) {
return arr;
}
moveStrToEndMapEntryWithCount(
List<MapEntry<String, int>> moveStrToEndMapEntryWithCount(
List<MapEntry<String, int>> arr, MapEntry<String, int> str,
{MapEntry<String, int>? strB}) {
MapEntry<String, int>? temp;
arr.removeWhere((element) {
bool res = element.key == str.key || element.key == strB?.key;
if (res) {
temp = element;
bool resA = element.key == str.key;
bool resB = element.key == strB?.key;
if (resA) {
temp = str;
} else if (resB) {
temp = strB;
}
return res;
return resA || resB;
});
if (temp != null) {
arr = [...arr, temp!];
@ -364,6 +368,9 @@ class AppsProvider with ChangeNotifier {
Future<bool> canInstallSilently(
App app, SettingsProvider settingsProvider) async {
if (app.id == obtainiumId) {
return false;
}
if (!settingsProvider.enableBackgroundUpdates) {
return false;
}
@ -393,7 +400,7 @@ class AppsProvider with ChangeNotifier {
(await getInstalledInfo(app.id))?.applicationInfo?.targetSdkVersion;
// The OS must also be new enough and the APK should target a new enough API
return osInfo.version.sdkInt >= 30 &&
return osInfo.version.sdkInt >= 31 &&
targetSDK != null &&
targetSDK >= // https://developer.android.com/reference/android/content/pm/PackageInstaller.SessionParams#setRequireUserAction(int)
(osInfo.version.sdkInt - 3);
@ -1331,67 +1338,75 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
(netResult != ConnectivityResult.ethernet);
}
// Loop through all updates and check each
for (int i = 0; i < toCheck.length; i++) {
var appId = toCheck[i].key;
var retryCount = toCheck[i].value;
AppInMemory? app = appsProvider.apps[appId];
if (app?.app.installedVersion != null) {
try {
notificationsProvider.notify(
notif = CheckingUpdatesNotification(app?.name ?? appId),
cancelExisting: true);
App? newApp = await appsProvider.checkUpdate(appId);
if (newApp != null) {
if (networkRestricted ||
!(await appsProvider.canInstallSilently(
app!.app, settingsProvider))) {
notificationsProvider.notify(
UpdateNotification([newApp], id: newApp.id.hashCode - 1));
List<App> toNotify = [];
try {
for (int i = 0; i < toCheck.length; i++) {
var appId = toCheck[i].key;
var attemptCount = toCheck[i].value + 1;
AppInMemory? app = appsProvider.apps[appId];
if (app?.app.installedVersion != null) {
try {
notificationsProvider.notify(
notif = CheckingUpdatesNotification(app?.name ?? appId),
cancelExisting: true);
App? newApp = await appsProvider.checkUpdate(appId);
if (newApp != null) {
if (networkRestricted ||
!(await appsProvider.canInstallSilently(
app!.app, settingsProvider))) {
toNotify.add(newApp);
} else {
toInstall.add(MapEntry(appId, 0));
}
}
if (i == (toCheck.length - 1)) {
didCompleteChecking = true;
}
} catch (e) {
// If you got an error, move the offender to the back of the line (increment their fail count) and schedule another task to continue checking shortly
logs.add(
'BG update task $taskId: Got error on checking for $appId \'${e.toString()}\'.');
if (attemptCount < maxAttempts) {
var remainingSeconds = e is RateLimitError
? (i == 0 ? (e.remainingMinutes * 60) : (5 * 60))
: e is ClientException
? (15 * 60)
: pow(attemptCount, 2).toInt();
logs.add(
'BG update task $taskId: Will continue in $remainingSeconds seconds (with $appId moved to the end of the line).');
var remainingToCheck = moveStrToEndMapEntryWithCount(
toCheck.sublist(i), MapEntry(appId, attemptCount));
AndroidAlarmManager.oneShot(Duration(seconds: remainingSeconds),
taskId + 1, bgUpdateCheck,
params: {
'toCheck': remainingToCheck
.map(
(entry) => {'key': entry.key, 'value': entry.value})
.toList(),
'toInstall': toInstall
.map(
(entry) => {'key': entry.key, 'value': entry.value})
.toList(),
});
break;
} else {
toInstall.add(MapEntry(appId, 0));
// If the offender has reached its fail limit, notify the user and remove it from the list (task can continue)
toCheck.removeAt(i);
i--;
notificationsProvider
.notify(ErrorCheckingUpdatesNotification(e.toString()));
}
} finally {
if (notif != null) {
notificationsProvider.cancel(notif.id);
}
}
if (i == (toCheck.length - 1)) {
didCompleteChecking = true;
}
} catch (e) {
// If you got an error, move the offender to the back of the line (increment their fail count) and schedule another task to continue checking shortly
logs.add(
'BG update task $taskId: Got error on checking for $appId \'${e.toString()}\'.');
if (retryCount < maxAttempts) {
var remainingSeconds = e is RateLimitError
? (i == 0 ? (e.remainingMinutes * 60) : (5 * 60))
: e is ClientException
? (15 * 60)
: (retryCount ^ 2);
logs.add(
'BG update task $taskId: Will continue in $remainingSeconds seconds (with $appId moved to the end of the line).');
var remainingToCheck = moveStrToEndMapEntryWithCount(
toCheck.sublist(i), MapEntry(appId, retryCount + 1));
AndroidAlarmManager.oneShot(
Duration(seconds: remainingSeconds), taskId + 1, bgUpdateCheck,
params: {
'toCheck': remainingToCheck
.map((entry) => {'key': entry.key, 'value': entry.value})
.toList(),
'toInstall': toInstall
.map((entry) => {'key': entry.key, 'value': entry.value})
.toList(),
});
break;
} else {
// If the offender has reached its fail limit, notify the user and remove it from the list (task can continue)
toCheck.removeAt(i);
i--;
notificationsProvider
.notify(ErrorCheckingUpdatesNotification(e.toString()));
}
} finally {
if (notif != null) {
notificationsProvider.cancel(notif.id);
}
}
}
} finally {
if (toNotify.isNotEmpty) {
notificationsProvider.notify(UpdateNotification(toNotify));
}
}
// If you're done checking and found some silently installable updates, schedule another task which will run in install mode
if (didCompleteChecking && toInstall.isNotEmpty) {

View File

@ -9,6 +9,7 @@ import 'package:html/dom.dart';
import 'package:http/http.dart';
import 'package:obtainium/app_sources/apkmirror.dart';
import 'package:obtainium/app_sources/apkpure.dart';
import 'package:obtainium/app_sources/aptoide.dart';
import 'package:obtainium/app_sources/codeberg.dart';
import 'package:obtainium/app_sources/fdroid.dart';
import 'package:obtainium/app_sources/fdroidrepo.dart';
@ -325,6 +326,7 @@ abstract class AppSource {
bool enforceTrackOnly = false;
bool changeLogIfAnyIsMarkDown = true;
bool appIdInferIsOptional = false;
bool allowSubDomains = false;
AppSource() {
name = runtimeType.toString();
@ -522,13 +524,14 @@ class SourceProvider {
Jenkins(),
SourceForge(),
SourceHut(),
Aptoide(),
APKMirror(),
APKPure(),
HuaweiAppGallery(),
// APKCombo(), // Can't get past their scraping blocking yet (get 403 Forbidden)
Mullvad(),
Signal(),
VLC(), // As of 2023-08-26 this site randomly messes up the 'latest' version (one minute it's 3.5.4, next minute back to 3.5.3)
VLC(),
// WhatsApp(), // As of 2023-03-20 this is unusable as the version on the webpage is months out of date
TelegramApp(),
SteamMobile(),
@ -554,7 +557,9 @@ class SourceProvider {
}
AppSource? source;
for (var s in sources.where((element) => element.host != null)) {
if (RegExp('://${s.host}(/|\\z)?').hasMatch(url)) {
if (RegExp(
'://${s.allowSubDomains ? '([^\\.]+\\.)*' : ''}${s.host}(/|\\z)?')
.hasMatch(url)) {
source = s;
break;
}

View File

@ -46,10 +46,10 @@ packages:
dependency: transitive
description:
name: archive
sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
sha256: "49b1fad315e57ab0bbc15bcbb874e83116a1d78f77ebd500a4af6c9407d6b28e"
url: "https://pub.dev"
source: hosted
version: "3.3.7"
version: "3.3.8"
args:
dependency: transitive
description:
@ -206,10 +206,10 @@ packages:
dependency: "direct main"
description:
name: easy_localization
sha256: "30ebf25448ffe169e0bd9bc4b5da94faa8398967a2ad2ca09f438be8b6953645"
sha256: de63e3b422adfc97f256cbb3f8cf12739b6a4993d390f3cadb3f51837afaefe5
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.0.3"
easy_logger:
dependency: transitive
description:
@ -783,18 +783,18 @@ packages:
dependency: "direct main"
description:
name: url_launcher
sha256: "38d8e783681bc342e92dc9799cbe4421d08c4d210a67ee9d61d0f7310491a465"
sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27"
url: "https://pub.dev"
source: hosted
version: "6.1.13"
version: "6.1.14"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: ef7e34951ffa963fb7a65928deeb38d40fb3c5975baf93c1d631341ff7f2650a
sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330
url: "https://pub.dev"
source: hosted
version: "6.0.39"
version: "6.1.0"
url_launcher_ios:
dependency: transitive
description:
@ -823,10 +823,10 @@ packages:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "4d0dae953f80dc06fa24c58d0f8381302139c22c2dad301417787ad96f5f73bd"
sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.5"
url_launcher_web:
dependency: transitive
description:
@ -879,10 +879,10 @@ packages:
dependency: transitive
description:
name: webview_flutter_android
sha256: "8545719bbf06f5c4b850f0d5f86da1fe837b1953c56b9af579a26be73627c98d"
sha256: "0d8f5ac96a155e672129bf94c7abf625de01241d44d269dbaff083f1b4deb1aa"
url: "https://pub.dev"
source: hosted
version: "3.9.4"
version: "3.9.5"
webview_flutter_platform_interface:
dependency: transitive
description:

View File

@ -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
# 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.
version: 0.14.5+197 # When changing this, update the tag in main() accordingly
version: 0.14.10+202 # When changing this, update the tag in main() accordingly
environment:
sdk: '>=2.18.2 <3.0.0'