mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-13 21:36:42 +02:00
Compare commits
22 Commits
v0.14.5-be
...
v0.14.10-b
Author | SHA1 | Date | |
---|---|---|---|
8ba0a0a776 | |||
73ed0cea88 | |||
58a378d212 | |||
553307ba70 | |||
78f73a9049 | |||
abc69e7a0e | |||
503914dbce | |||
69680f8680 | |||
1a616aacb7 | |||
52d19f267e | |||
75a8dc9ee4 | |||
8c63920ef6 | |||
dda5619cdc | |||
9cc25fd931 | |||
f5a9eb4295 | |||
ebe256e482 | |||
e215585a64 | |||
8b01fc03ec | |||
9af2c8370d | |||
01f9003b8d | |||
73a3c7eb71 | |||
370ec1432e |
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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.
|
29
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal 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.
|
@ -16,6 +16,7 @@ Currently supported App sources:
|
|||||||
- [Signal](https://signal.org/)
|
- [Signal](https://signal.org/)
|
||||||
- [SourceForge](https://sourceforge.net/)
|
- [SourceForge](https://sourceforge.net/)
|
||||||
- [SourceHut](https://git.sr.ht/)
|
- [SourceHut](https://git.sr.ht/)
|
||||||
|
- [Aptoide](https://aptoide.com/)
|
||||||
- [APKMirror](https://apkmirror.com/) (Track-Only)
|
- [APKMirror](https://apkmirror.com/) (Track-Only)
|
||||||
- [APKPure](https://apkpure.com/)
|
- [APKPure](https://apkpure.com/)
|
||||||
- [Huawei AppGallery](https://appgallery.huawei.com/)
|
- [Huawei AppGallery](https://appgallery.huawei.com/)
|
||||||
|
@ -252,6 +252,7 @@
|
|||||||
"intermediateLinkNotFound": "Link intermediário não encontrado",
|
"intermediateLinkNotFound": "Link intermediário não encontrado",
|
||||||
"exemptFromBackgroundUpdates": "Isento de atualizações em segundo plano (se ativadas)",
|
"exemptFromBackgroundUpdates": "Isento de atualizações em segundo plano (se ativadas)",
|
||||||
"bgUpdatesOnWiFiOnly": "Desative atualizações em segundo plano quando não estiver em WiFi",
|
"bgUpdatesOnWiFiOnly": "Desative atualizações em segundo plano quando não estiver em WiFi",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remover App?",
|
"one": "Remover App?",
|
||||||
"other": "Remover Apps?"
|
"other": "Remover Apps?"
|
||||||
|
@ -249,6 +249,7 @@
|
|||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"verifyLatestTag": "Verify the 'latest' tag",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Želite li ukloniti aplikaciju?",
|
"one": "Želite li ukloniti aplikaciju?",
|
||||||
"other": "Želite li ukloniti aplikacije?"
|
"other": "Želite li ukloniti aplikacije?"
|
||||||
|
@ -249,6 +249,7 @@
|
|||||||
"verifyLatestTag": "Überprüfe das 'latest' Tag",
|
"verifyLatestTag": "Überprüfe das 'latest' Tag",
|
||||||
"exemptFromBackgroundUpdates": "Ausschluss von Hintergrundaktualisierungen (falls aktiviert)",
|
"exemptFromBackgroundUpdates": "Ausschluss von Hintergrundaktualisierungen (falls aktiviert)",
|
||||||
"bgUpdatesOnWiFiOnly": "Hintergrundaktualisierungen deaktivieren, wenn kein WLAN vorhanden ist",
|
"bgUpdatesOnWiFiOnly": "Hintergrundaktualisierungen deaktivieren, wenn kein WLAN vorhanden ist",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "App entfernen?",
|
"one": "App entfernen?",
|
||||||
"other": "Apps entfernen?"
|
"other": "Apps entfernen?"
|
||||||
|
@ -252,6 +252,7 @@
|
|||||||
"intermediateLinkNotFound": "Intermediate link not found",
|
"intermediateLinkNotFound": "Intermediate link not found",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remove App?",
|
"one": "Remove App?",
|
||||||
"other": "Remove Apps?"
|
"other": "Remove Apps?"
|
||||||
|
@ -249,6 +249,7 @@
|
|||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"verifyLatestTag": "Verify the 'latest' tag",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "¿Eliminar Aplicación?",
|
"one": "¿Eliminar Aplicación?",
|
||||||
"other": "¿Eliminar Aplicaciones?"
|
"other": "¿Eliminar Aplicaciones?"
|
||||||
|
@ -249,6 +249,7 @@
|
|||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"verifyLatestTag": "Verify the 'latest' tag",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "برنامه حذف شود؟",
|
"one": "برنامه حذف شود؟",
|
||||||
"other": "برنامه ها حذف شوند؟"
|
"other": "برنامه ها حذف شوند؟"
|
||||||
|
@ -249,6 +249,7 @@
|
|||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"verifyLatestTag": "Verify the 'latest' tag",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Supprimer l'application ?",
|
"one": "Supprimer l'application ?",
|
||||||
"other": "Supprimer les applications ?"
|
"other": "Supprimer les applications ?"
|
||||||
|
@ -87,7 +87,7 @@
|
|||||||
"author": "Szerző",
|
"author": "Szerző",
|
||||||
"upToDateApps": "Naprakész appok",
|
"upToDateApps": "Naprakész appok",
|
||||||
"nonInstalledApps": "Nem telepített appok",
|
"nonInstalledApps": "Nem telepített appok",
|
||||||
"importExport": "Importálás/Exportálás",
|
"importExport": "Import/Export",
|
||||||
"settings": "Beállítások",
|
"settings": "Beállítások",
|
||||||
"exportedTo": "Exportálva ide {}",
|
"exportedTo": "Exportálva ide {}",
|
||||||
"obtainiumExport": "Obtainium Adat Exportálás",
|
"obtainiumExport": "Obtainium Adat Exportálás",
|
||||||
@ -137,7 +137,7 @@
|
|||||||
"share": "Megosztás",
|
"share": "Megosztás",
|
||||||
"appNotFound": "App nem található",
|
"appNotFound": "App nem található",
|
||||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
||||||
"pickAnAPK": "Válasszon egy APK-ot",
|
"pickAnAPK": "Válasszon egy APK-t",
|
||||||
"appHasMoreThanOnePackage": "A(z) {} egynél több csomaggal rendelkezik:",
|
"appHasMoreThanOnePackage": "A(z) {} egynél több csomaggal rendelkezik:",
|
||||||
"deviceSupportsXArch": "Eszköze támogatja a {} CPU architektúrát.",
|
"deviceSupportsXArch": "Eszköze támogatja a {} CPU architektúrát.",
|
||||||
"deviceSupportsFollowingArchs": "Az eszköze a következő CPU architektúrákat támogatja:",
|
"deviceSupportsFollowingArchs": "Az eszköze a következő CPU architektúrákat támogatja:",
|
||||||
@ -204,7 +204,7 @@
|
|||||||
"copiedToClipboard": "Másolva a vágólapra",
|
"copiedToClipboard": "Másolva a vágólapra",
|
||||||
"storagePermissionDenied": "Tárhely engedély megtagadva",
|
"storagePermissionDenied": "Tárhely engedély megtagadva",
|
||||||
"selectedCategorizeWarning": "Ez felváltja a kiválasztott alkalmazások meglévő kategória-beállításait.",
|
"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",
|
"removeFromObtainium": "Eltávolítás az Obtainiumból",
|
||||||
"uninstallFromDevice": "Eltávolítás a készülékrő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.",
|
"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.",
|
"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",
|
"sortByFileNamesNotLinks": "Fájlnevek szerinti elrendezés teljes linkek helyett",
|
||||||
"filterReleaseNotesByRegEx": "Kiadási megjegyzések szűrése reguláris kifejezéssel",
|
"filterReleaseNotesByRegEx": "Kiadási megjegyzések szűrése reguláris kifejezéssel",
|
||||||
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
|
"customLinkFilterRegex": "Egyéni APK hivatkozásszűrő reguláris kifejezéssel (Alapérték '.apk$')",
|
||||||
"appsPossiblyUpdated": "App Updates Attempted",
|
"appsPossiblyUpdated": "App frissítési kísérlet",
|
||||||
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
|
"appsPossiblyUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy vagy több alkalmazás frissítése lehetséges a háttérben",
|
||||||
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
|
"xWasPossiblyUpdatedToY": "{} frissítve lehet erre {}.",
|
||||||
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
|
"backgroundUpdateReqsExplanation": "Előfordulhat, hogy nem minden appnál lehetséges a háttérbeli frissítés.",
|
||||||
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
|
"backgroundUpdateLimitsExplanation": "A háttérben történő telepítés sikeressége csak az Obtainium megnyitásakor állapítható meg.",
|
||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"verifyLatestTag": "Ellenőrizze a „legújabb” címkét",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"exemptFromBackgroundUpdates": "Mentes a háttérben történő frissítések alól (ha engedélyezett)",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"bgUpdatesOnWiFiOnly": "Tiltsa le a háttérben frissítéseket, ha nincs Wi-Fi-n",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Eltávolítja az alkalmazást?",
|
"one": "Eltávolítja az alkalmazást?",
|
||||||
"other": "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."
|
"other": "{} és {} további alkalmazás frissítve."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
"one": "{} and 1 more app may have been updated.",
|
"one": "{} és 1 további alkalmazás is frissült.",
|
||||||
"other": "{} and {} more apps may have been updated."
|
"other": "{} és {} további alkalmazás is frissült."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,6 +249,7 @@
|
|||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"verifyLatestTag": "Verify the 'latest' tag",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Rimuovere l'app?",
|
"one": "Rimuovere l'app?",
|
||||||
"other": "Rimuovere le app?"
|
"other": "Rimuovere le app?"
|
||||||
|
@ -250,6 +250,7 @@
|
|||||||
"verifyLatestTag": "'latest'タグを確認する",
|
"verifyLatestTag": "'latest'タグを確認する",
|
||||||
"exemptFromBackgroundUpdates": "バックグラウンドアップデートを行わない (有効な場合)",
|
"exemptFromBackgroundUpdates": "バックグラウンドアップデートを行わない (有効な場合)",
|
||||||
"bgUpdatesOnWiFiOnly": "WiFiを使用していない場合,バックグラウンドアップデートを無効にする",
|
"bgUpdatesOnWiFiOnly": "WiFiを使用していない場合,バックグラウンドアップデートを無効にする",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "アプリを削除しますか?",
|
"one": "アプリを削除しますか?",
|
||||||
"other": "アプリを削除しますか?"
|
"other": "アプリを削除しますか?"
|
||||||
|
@ -255,6 +255,7 @@
|
|||||||
"verifyLatestTag": "Zweryfikuj najnowszy tag",
|
"verifyLatestTag": "Zweryfikuj najnowszy tag",
|
||||||
"exemptFromBackgroundUpdates": "Wyklucz z uaktualnień w tle (jeśli są włączone)",
|
"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",
|
"bgUpdatesOnWiFiOnly": "Wyłącz aktualizacje w tle, gdy nie ma połączenia z Wi-Fi",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Usunąć aplikację?",
|
"one": "Usunąć aplikację?",
|
||||||
"few": "Usunąć aplikacje?",
|
"few": "Usunąć aplikacje?",
|
||||||
|
@ -249,6 +249,7 @@
|
|||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"verifyLatestTag": "Verify the 'latest' tag",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Удалить приложение?",
|
"one": "Удалить приложение?",
|
||||||
"other": "Удалить приложения?"
|
"other": "Удалить приложения?"
|
||||||
|
@ -250,6 +250,7 @@
|
|||||||
"verifyLatestTag": "验证“Latest”标签",
|
"verifyLatestTag": "验证“Latest”标签",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
||||||
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "是否删除应用?",
|
"one": "是否删除应用?",
|
||||||
"other": "是否删除应用?"
|
"other": "是否删除应用?"
|
||||||
|
108
lib/app_sources/aptoide.dart
Normal file
108
lib/app_sources/aptoide.dart
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
|
import 'package:obtainium/components/generated_form.dart';
|
||||||
import 'package:obtainium/custom_errors.dart';
|
import 'package:obtainium/custom_errors.dart';
|
||||||
import 'package:obtainium/providers/source_provider.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
|
|
||||||
@ -11,6 +12,12 @@ class FDroid extends AppSource {
|
|||||||
host = 'f-droid.org';
|
host = 'f-droid.org';
|
||||||
name = tr('fdroid');
|
name = tr('fdroid');
|
||||||
canSearch = true;
|
canSearch = true;
|
||||||
|
additionalSourceAppSpecificSettingFormItems = [
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch('autoSelectHighestVersionCode',
|
||||||
|
label: tr('autoSelectHighestVersionCode'))
|
||||||
|
]
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -37,7 +44,8 @@ class FDroid extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
|
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||||
Response res, String apkUrlPrefix, String standardUrl) {
|
Response res, String apkUrlPrefix, String standardUrl,
|
||||||
|
{bool autoSelectHighestVersionCode = false}) {
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
List<dynamic> releases = jsonDecode(res.body)['packages'] ?? [];
|
List<dynamic> releases = jsonDecode(res.body)['packages'] ?? [];
|
||||||
if (releases.isEmpty) {
|
if (releases.isEmpty) {
|
||||||
@ -47,8 +55,12 @@ class FDroid extends AppSource {
|
|||||||
if (latestVersion == null) {
|
if (latestVersion == null) {
|
||||||
throw NoVersionError();
|
throw NoVersionError();
|
||||||
}
|
}
|
||||||
List<String> apkUrls = releases
|
Iterable<dynamic> latestReleases =
|
||||||
.where((element) => element['versionName'] == latestVersion)
|
releases.where((element) => element['versionName'] == latestVersion);
|
||||||
|
if (latestReleases.length > 1 && autoSelectHighestVersionCode) {
|
||||||
|
latestReleases = [latestReleases.first];
|
||||||
|
}
|
||||||
|
List<String> apkUrls = latestReleases
|
||||||
.map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
|
.map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
|
||||||
.toList();
|
.toList();
|
||||||
return APKDetails(latestVersion, getApkUrlsFromUrls(apkUrls),
|
return APKDetails(latestVersion, getApkUrlsFromUrls(apkUrls),
|
||||||
@ -68,7 +80,9 @@ class FDroid extends AppSource {
|
|||||||
return getAPKUrlsFromFDroidPackagesAPIResponse(
|
return getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||||
await sourceRequest('https://$host/api/v1/packages/$appId'),
|
await sourceRequest('https://$host/api/v1/packages/$appId'),
|
||||||
'https://$host/repo/$appId',
|
'https://$host/repo/$appId',
|
||||||
standardUrl);
|
standardUrl,
|
||||||
|
autoSelectHighestVersionCode:
|
||||||
|
additionalSettings['autoSelectHighestVersionCode'] == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -18,7 +18,7 @@ String ensureAbsoluteUrl(String ambiguousUrl, Uri referenceAbsoluteUrl) {
|
|||||||
.toList();
|
.toList();
|
||||||
if (ambiguousUrl.startsWith('/') || currPathSegments.isEmpty) {
|
if (ambiguousUrl.startsWith('/') || currPathSegments.isEmpty) {
|
||||||
return '${referenceAbsoluteUrl.origin}/$ambiguousUrl';
|
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';
|
return '${referenceAbsoluteUrl.origin}/${currPathSegments.join('/')}/$ambiguousUrl';
|
||||||
} else {
|
} else {
|
||||||
return '${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - 1).join('/')}/$ambiguousUrl';
|
return '${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - 1).join('/')}/$ambiguousUrl';
|
||||||
|
@ -3,8 +3,13 @@ import 'package:obtainium/custom_errors.dart';
|
|||||||
import 'package:obtainium/providers/source_provider.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
|
|
||||||
class IzzyOnDroid extends AppSource {
|
class IzzyOnDroid extends AppSource {
|
||||||
|
late FDroid fd;
|
||||||
|
|
||||||
IzzyOnDroid() {
|
IzzyOnDroid() {
|
||||||
host = 'android.izzysoft.de';
|
host = 'android.izzysoft.de';
|
||||||
|
fd = FDroid();
|
||||||
|
additionalSourceAppSpecificSettingFormItems =
|
||||||
|
fd.additionalSourceAppSpecificSettingFormItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -20,7 +25,7 @@ class IzzyOnDroid extends AppSource {
|
|||||||
@override
|
@override
|
||||||
Future<String?> tryInferringAppId(String standardUrl,
|
Future<String?> tryInferringAppId(String standardUrl,
|
||||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||||
return FDroid().tryInferringAppId(standardUrl);
|
return fd.tryInferringAppId(standardUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -29,10 +34,12 @@ class IzzyOnDroid extends AppSource {
|
|||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
String? appId = await tryInferringAppId(standardUrl);
|
String? appId = await tryInferringAppId(standardUrl);
|
||||||
return FDroid().getAPKUrlsFromFDroidPackagesAPIResponse(
|
return fd.getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||||
await sourceRequest(
|
await sourceRequest(
|
||||||
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),
|
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),
|
||||||
'https://android.izzysoft.de/frepo/$appId',
|
'https://android.izzysoft.de/frepo/$appId',
|
||||||
standardUrl);
|
standardUrl,
|
||||||
|
autoSelectHighestVersionCode:
|
||||||
|
additionalSettings['autoSelectHighestVersionCode'] == true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
|
|||||||
// ignore: implementation_imports
|
// ignore: implementation_imports
|
||||||
import 'package:easy_localization/src/localization.dart';
|
import 'package:easy_localization/src/localization.dart';
|
||||||
|
|
||||||
const String currentVersion = '0.14.5';
|
const String currentVersion = '0.14.10';
|
||||||
const String currentReleaseTag =
|
const String currentReleaseTag =
|
||||||
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
|
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
|
||||||
import 'package:android_intent_plus/flag.dart';
|
import 'package:android_intent_plus/flag.dart';
|
||||||
@ -116,16 +117,19 @@ moveStrToEnd(List<String> arr, String str, {String? strB}) {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
moveStrToEndMapEntryWithCount(
|
List<MapEntry<String, int>> moveStrToEndMapEntryWithCount(
|
||||||
List<MapEntry<String, int>> arr, MapEntry<String, int> str,
|
List<MapEntry<String, int>> arr, MapEntry<String, int> str,
|
||||||
{MapEntry<String, int>? strB}) {
|
{MapEntry<String, int>? strB}) {
|
||||||
MapEntry<String, int>? temp;
|
MapEntry<String, int>? temp;
|
||||||
arr.removeWhere((element) {
|
arr.removeWhere((element) {
|
||||||
bool res = element.key == str.key || element.key == strB?.key;
|
bool resA = element.key == str.key;
|
||||||
if (res) {
|
bool resB = element.key == strB?.key;
|
||||||
temp = element;
|
if (resA) {
|
||||||
|
temp = str;
|
||||||
|
} else if (resB) {
|
||||||
|
temp = strB;
|
||||||
}
|
}
|
||||||
return res;
|
return resA || resB;
|
||||||
});
|
});
|
||||||
if (temp != null) {
|
if (temp != null) {
|
||||||
arr = [...arr, temp!];
|
arr = [...arr, temp!];
|
||||||
@ -364,6 +368,9 @@ class AppsProvider with ChangeNotifier {
|
|||||||
|
|
||||||
Future<bool> canInstallSilently(
|
Future<bool> canInstallSilently(
|
||||||
App app, SettingsProvider settingsProvider) async {
|
App app, SettingsProvider settingsProvider) async {
|
||||||
|
if (app.id == obtainiumId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!settingsProvider.enableBackgroundUpdates) {
|
if (!settingsProvider.enableBackgroundUpdates) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -393,7 +400,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
(await getInstalledInfo(app.id))?.applicationInfo?.targetSdkVersion;
|
(await getInstalledInfo(app.id))?.applicationInfo?.targetSdkVersion;
|
||||||
|
|
||||||
// The OS must also be new enough and the APK should target a new enough API
|
// 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 != null &&
|
||||||
targetSDK >= // https://developer.android.com/reference/android/content/pm/PackageInstaller.SessionParams#setRequireUserAction(int)
|
targetSDK >= // https://developer.android.com/reference/android/content/pm/PackageInstaller.SessionParams#setRequireUserAction(int)
|
||||||
(osInfo.version.sdkInt - 3);
|
(osInfo.version.sdkInt - 3);
|
||||||
@ -1331,67 +1338,75 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
|||||||
(netResult != ConnectivityResult.ethernet);
|
(netResult != ConnectivityResult.ethernet);
|
||||||
}
|
}
|
||||||
// Loop through all updates and check each
|
// Loop through all updates and check each
|
||||||
for (int i = 0; i < toCheck.length; i++) {
|
List<App> toNotify = [];
|
||||||
var appId = toCheck[i].key;
|
try {
|
||||||
var retryCount = toCheck[i].value;
|
for (int i = 0; i < toCheck.length; i++) {
|
||||||
AppInMemory? app = appsProvider.apps[appId];
|
var appId = toCheck[i].key;
|
||||||
if (app?.app.installedVersion != null) {
|
var attemptCount = toCheck[i].value + 1;
|
||||||
try {
|
AppInMemory? app = appsProvider.apps[appId];
|
||||||
notificationsProvider.notify(
|
if (app?.app.installedVersion != null) {
|
||||||
notif = CheckingUpdatesNotification(app?.name ?? appId),
|
try {
|
||||||
cancelExisting: true);
|
notificationsProvider.notify(
|
||||||
App? newApp = await appsProvider.checkUpdate(appId);
|
notif = CheckingUpdatesNotification(app?.name ?? appId),
|
||||||
if (newApp != null) {
|
cancelExisting: true);
|
||||||
if (networkRestricted ||
|
App? newApp = await appsProvider.checkUpdate(appId);
|
||||||
!(await appsProvider.canInstallSilently(
|
if (newApp != null) {
|
||||||
app!.app, settingsProvider))) {
|
if (networkRestricted ||
|
||||||
notificationsProvider.notify(
|
!(await appsProvider.canInstallSilently(
|
||||||
UpdateNotification([newApp], id: newApp.id.hashCode - 1));
|
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 {
|
} 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 you're done checking and found some silently installable updates, schedule another task which will run in install mode
|
||||||
if (didCompleteChecking && toInstall.isNotEmpty) {
|
if (didCompleteChecking && toInstall.isNotEmpty) {
|
||||||
|
@ -9,6 +9,7 @@ import 'package:html/dom.dart';
|
|||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:obtainium/app_sources/apkmirror.dart';
|
import 'package:obtainium/app_sources/apkmirror.dart';
|
||||||
import 'package:obtainium/app_sources/apkpure.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/codeberg.dart';
|
||||||
import 'package:obtainium/app_sources/fdroid.dart';
|
import 'package:obtainium/app_sources/fdroid.dart';
|
||||||
import 'package:obtainium/app_sources/fdroidrepo.dart';
|
import 'package:obtainium/app_sources/fdroidrepo.dart';
|
||||||
@ -325,6 +326,7 @@ abstract class AppSource {
|
|||||||
bool enforceTrackOnly = false;
|
bool enforceTrackOnly = false;
|
||||||
bool changeLogIfAnyIsMarkDown = true;
|
bool changeLogIfAnyIsMarkDown = true;
|
||||||
bool appIdInferIsOptional = false;
|
bool appIdInferIsOptional = false;
|
||||||
|
bool allowSubDomains = false;
|
||||||
|
|
||||||
AppSource() {
|
AppSource() {
|
||||||
name = runtimeType.toString();
|
name = runtimeType.toString();
|
||||||
@ -522,13 +524,14 @@ class SourceProvider {
|
|||||||
Jenkins(),
|
Jenkins(),
|
||||||
SourceForge(),
|
SourceForge(),
|
||||||
SourceHut(),
|
SourceHut(),
|
||||||
|
Aptoide(),
|
||||||
APKMirror(),
|
APKMirror(),
|
||||||
APKPure(),
|
APKPure(),
|
||||||
HuaweiAppGallery(),
|
HuaweiAppGallery(),
|
||||||
// APKCombo(), // Can't get past their scraping blocking yet (get 403 Forbidden)
|
// APKCombo(), // Can't get past their scraping blocking yet (get 403 Forbidden)
|
||||||
Mullvad(),
|
Mullvad(),
|
||||||
Signal(),
|
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
|
// WhatsApp(), // As of 2023-03-20 this is unusable as the version on the webpage is months out of date
|
||||||
TelegramApp(),
|
TelegramApp(),
|
||||||
SteamMobile(),
|
SteamMobile(),
|
||||||
@ -554,7 +557,9 @@ class SourceProvider {
|
|||||||
}
|
}
|
||||||
AppSource? source;
|
AppSource? source;
|
||||||
for (var s in sources.where((element) => element.host != null)) {
|
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;
|
source = s;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
24
pubspec.lock
24
pubspec.lock
@ -46,10 +46,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
|
sha256: "49b1fad315e57ab0bbc15bcbb874e83116a1d78f77ebd500a4af6c9407d6b28e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.7"
|
version: "3.3.8"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -206,10 +206,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: easy_localization
|
name: easy_localization
|
||||||
sha256: "30ebf25448ffe169e0bd9bc4b5da94faa8398967a2ad2ca09f438be8b6953645"
|
sha256: de63e3b422adfc97f256cbb3f8cf12739b6a4993d390f3cadb3f51837afaefe5
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
easy_logger:
|
easy_logger:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -783,18 +783,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
sha256: "38d8e783681bc342e92dc9799cbe4421d08c4d210a67ee9d61d0f7310491a465"
|
sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.13"
|
version: "6.1.14"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
sha256: ef7e34951ffa963fb7a65928deeb38d40fb3c5975baf93c1d631341ff7f2650a
|
sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.39"
|
version: "6.1.0"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -823,10 +823,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_platform_interface
|
name: url_launcher_platform_interface
|
||||||
sha256: "4d0dae953f80dc06fa24c58d0f8381302139c22c2dad301417787ad96f5f73bd"
|
sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.5"
|
||||||
url_launcher_web:
|
url_launcher_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -879,10 +879,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_android
|
name: webview_flutter_android
|
||||||
sha256: "8545719bbf06f5c4b850f0d5f86da1fe837b1953c56b9af579a26be73627c98d"
|
sha256: "0d8f5ac96a155e672129bf94c7abf625de01241d44d269dbaff083f1b4deb1aa"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.9.4"
|
version: "3.9.5"
|
||||||
webview_flutter_platform_interface:
|
webview_flutter_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -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.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:
|
environment:
|
||||||
sdk: '>=2.18.2 <3.0.0'
|
sdk: '>=2.18.2 <3.0.0'
|
||||||
|
Reference in New Issue
Block a user