mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-16 06:36:44 +02:00
Compare commits
31 Commits
v0.14.23-b
...
v0.14.26-b
Author | SHA1 | Date | |
---|---|---|---|
c0fffa6293 | |||
d800d3ba41 | |||
d267343c64 | |||
0f72e37272 | |||
89e44d581f | |||
c908a710de | |||
f0809cde72 | |||
e6da8c1c18 | |||
81ea67afa8 | |||
488049821e | |||
fc7408a8f8 | |||
6955191fdd | |||
ba59693d90 | |||
ce6e51128d | |||
a7a26037ae | |||
00854abd1a | |||
c26649d5f2 | |||
63b76a249f | |||
95f19208ad | |||
f4a902ee47 | |||
036823cbb6 | |||
547c2c8c0d | |||
7453d19d27 | |||
2a0b68d637 | |||
02f374fdf0 | |||
3536dcd775 | |||
6fa887e536 | |||
71755763b9 | |||
5aabcccfd4 | |||
1e009e2211 | |||
811bc6434b |
18
.github/workflows/release.yml
vendored
18
.github/workflows/release.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Release (as Draft)
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@ -42,18 +42,16 @@ jobs:
|
||||
done
|
||||
rm apksign.keystore
|
||||
PGP_KEY_FINGERPRINT="${{ steps.import_pgp_key.outputs.fingerprint }}"
|
||||
gpg --batch --yes --delete-secret-keys "$PGP_KEY_FINGERPRINT"
|
||||
gpg --batch --yes --delete-keys "$PGP_KEY_FINGERPRINT"
|
||||
|
||||
- name: Extract Version
|
||||
id: extract_version
|
||||
run: |
|
||||
VERSION=$(grep -oP "currentVersion = '\K[^']+" lib/main.dart)
|
||||
echo "::set-output name=version::$VERSION"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
TAG=$(grep -oP "'.*\\\$currentVersion.*'" lib/main.dart | head -c -2 | tail -c +2 | sed "s/\$currentVersion/$VERSION/g")
|
||||
echo "::set-output name=tag::$TAG"
|
||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||
if [ -n "$(echo $TAG | grep -oP '\-beta$')" ]; then BETA=true; else BETA=false; fi
|
||||
echo "::set-output name=beta::$BETA"
|
||||
echo "beta=$BETA" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create Tag
|
||||
uses: mathieudutour/github-tag-action@v6.1
|
||||
@ -70,11 +68,3 @@ jobs:
|
||||
prerelease: "${{ steps.extract_version.outputs.beta }}"
|
||||
artifacts: ./build/app/outputs/flutter-apk/*-release*.apk*
|
||||
generateReleaseNotes: true
|
||||
draft: true
|
||||
|
||||
- name: Archive Reports For Job
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: reports
|
||||
path: '*/build/reports'
|
||||
if: ${{ always() }}
|
||||
|
@ -242,9 +242,12 @@
|
||||
"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 {}.",
|
||||
"enableBackgroundUpdates": "Enable background updates",
|
||||
"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",
|
||||
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
|
||||
"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",
|
||||
@ -312,4 +315,4 @@
|
||||
"one": "{} and 1 more app may have been updated.",
|
||||
"other": "{} and {} more apps may have been updated."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,9 +242,12 @@
|
||||
"appsPossiblyUpdated": "App Aktualisierungen wurden versucht",
|
||||
"appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Updates für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden",
|
||||
"xWasPossiblyUpdatedToY": "{} wurde möglicherweise aktualisiert auf {}.",
|
||||
"enableBackgroundUpdates": "Aktiviere Hintergrundaktualisierungen",
|
||||
"backgroundUpdateReqsExplanation": "Die Hintergrundaktualisierung ist möglicherweise nicht für alle Apps möglich.",
|
||||
"backgroundUpdateLimitsExplanation": "Der Erfolg einer Hintergrundinstallation kann nur festgestellt werden, wenn Obtainium geöffnet wird.",
|
||||
"verifyLatestTag": "Überprüfe das „latest“ Tag",
|
||||
"intermediateLinkRegex": "Filter für einen \"Zwischen\"-Link, der zuerst besucht werden soll",
|
||||
"intermediateLinkNotFound": "Zwischenlink nicht gefunden",
|
||||
"exemptFromBackgroundUpdates": "Ausschluss von Hintergrundaktualisierungen (falls aktiviert)",
|
||||
"bgUpdatesOnWiFiOnly": "Hintergrundaktualisierungen deaktivieren, wenn kein WLAN vorhanden ist",
|
||||
"autoSelectHighestVersionCode": "Automatisch höchste APK-Code-Version auswählen",
|
||||
@ -297,8 +300,8 @@
|
||||
"other": "{} Tage"
|
||||
},
|
||||
"clearedNLogsBeforeXAfterY": {
|
||||
"one": "{n} Log gelöscht (vorher = {vorher}, nachher = {nachher})",
|
||||
"other": "{n} Logs gelöscht (vorher = {vorher}, nachher = {nachher})"
|
||||
"one": "{n} Log gelöscht (vorher = {before}, nachher = {after})",
|
||||
"other": "{n} Logs gelöscht (vorher = {before}, nachher = {after})"
|
||||
},
|
||||
"xAndNMoreUpdatesAvailable": {
|
||||
"one": "{} und 1 weitere App haben Aktualisierungen.",
|
||||
|
@ -242,9 +242,12 @@
|
||||
"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 {}.",
|
||||
"enableBackgroundUpdates": "Enable background updates",
|
||||
"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",
|
||||
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
|
||||
"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",
|
||||
|
@ -242,9 +242,12 @@
|
||||
"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 {}.",
|
||||
"enableBackgroundUpdates": "Enable background updates",
|
||||
"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",
|
||||
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
|
||||
"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",
|
||||
|
@ -242,9 +242,12 @@
|
||||
"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 {}.",
|
||||
"enableBackgroundUpdates": "Enable background updates",
|
||||
"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",
|
||||
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
|
||||
"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",
|
||||
|
@ -194,6 +194,7 @@
|
||||
"categories": "Kategóriák",
|
||||
"category": "Kategória",
|
||||
"noCategory": "Nincs kategória",
|
||||
"noCategories": "No Categories",
|
||||
"deleteCategoryQuestion": "Törli a kategóriát?",
|
||||
"categoryDeleteWarning": "A(z) {} összes app kategorizálatlan állapotba kerül.",
|
||||
"addCategory": "Új kategória",
|
||||
@ -241,9 +242,12 @@
|
||||
"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 {}.",
|
||||
"enableBackgroundUpdates": "Enable background updates",
|
||||
"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",
|
||||
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
|
||||
"intermediateLinkNotFound": "Intermediate link not found",
|
||||
"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": "A legmagasabb verziószámú APK auto. kiválasztása",
|
||||
|
@ -242,9 +242,12 @@
|
||||
"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 {}.",
|
||||
"enableBackgroundUpdates": "Enable background updates",
|
||||
"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",
|
||||
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
|
||||
"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",
|
||||
|
@ -1,14 +1,4 @@
|
||||
{
|
||||
"noDescription": "Brak opisu",
|
||||
"no": "Nie",
|
||||
"okay": "Okej",
|
||||
"appId": "ID aplikacji",
|
||||
"bgUpdateGotErrorRetryInMinutes": {
|
||||
"one": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minutę",
|
||||
"few": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minuty",
|
||||
"many": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minut",
|
||||
"other": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minuty"
|
||||
},
|
||||
"invalidURLForSource": "Nieprawidłowy adres URL aplikacji {}",
|
||||
"noReleaseFound": "Nie można znaleźć odpowiedniego wydania",
|
||||
"noVersionFound": "Nie można określić wersji wydania",
|
||||
@ -26,6 +16,7 @@
|
||||
"fallbackToOlderReleases": "Powracaj do starszych wersji",
|
||||
"filterReleaseTitlesByRegEx": "Filtruj tytuły wydań wg. wyrażeń regularnych",
|
||||
"invalidRegEx": "Nieprawidłowe wyrażenie regularne",
|
||||
"noDescription": "Brak opisu",
|
||||
"cancel": "Anuluj",
|
||||
"continue": "Kontynuuj",
|
||||
"requiredInBrackets": "(Wymagane)",
|
||||
@ -75,6 +66,7 @@
|
||||
"installUpdateApps": "Instaluj/aktualizuj aplikacje",
|
||||
"installUpdateSelectedApps": "Zainstaluj/zaktualizuj wybrane aplikacje",
|
||||
"markXSelectedAppsAsUpdated": "Oznaczyć {} wybranych aplikacji jako zaktualizowane?",
|
||||
"no": "Nie",
|
||||
"yes": "Tak",
|
||||
"markSelectedAppsUpdated": "Oznacz wybrane aplikacje jako zaktualizowane",
|
||||
"pinToTop": "Przypnij",
|
||||
@ -111,6 +103,7 @@
|
||||
"importErrors": "Błędy importowania",
|
||||
"importedXOfYApps": "Zaimportowano {} z {} aplikacji.",
|
||||
"followingURLsHadErrors": "Następujące adresy URL zawierały błędy:",
|
||||
"okay": "Okej",
|
||||
"selectURL": "Wybierz adres URL",
|
||||
"selectURLs": "Wybierz adresy URL",
|
||||
"pick": "Wybierz",
|
||||
@ -179,6 +172,7 @@
|
||||
"yesMarkUpdated": "Tak, oznacz jako zaktualizowane",
|
||||
"fdroid": "Oficjalny F-Droid",
|
||||
"appIdOrName": "ID aplikacji lub nazwa",
|
||||
"appId": "ID aplikacji",
|
||||
"appWithIdOrNameNotFound": "Nie znaleziono aplikacji o tym identyfikatorze lub nazwie",
|
||||
"reposHaveMultipleApps": "Repozytoria mogą zawierać wiele aplikacji",
|
||||
"fdroidThirdPartyRepo": "Zewnętrzne repo F-Droid",
|
||||
@ -245,15 +239,18 @@
|
||||
"sortByFileNamesNotLinks": "Sortuj wg nazw plików zamiast pełnych linków",
|
||||
"filterReleaseNotesByRegEx": "Filtruj informacje o wersji według wyrażenia regularnego",
|
||||
"customLinkFilterRegex": "Filtruj linki APK według wyrażenia regularnego (domyślnie \".apk$\")",
|
||||
"appsPossiblyUpdated": "Informuj o próbach aktualizacji",
|
||||
"appsPossiblyUpdated": "Próbowano zaktualizować aplikację",
|
||||
"appsPossiblyUpdatedNotifDescription": "Powiadamiaj o potencjalnym zastosowaniu w tle aktualizacji jednej lub większej ilości aplikacji",
|
||||
"xWasPossiblyUpdatedToY": "{} być może zaktualizowano do {}.",
|
||||
"backgroundUpdateReqsExplanation": "Aktualizacje w tle mogą nie być możliwe dla wszystkich aplikacji.",
|
||||
"enableBackgroundUpdates": "Włącz aktualizacje w tle",
|
||||
"backgroundUpdateReqsExplanation": "Aktualizacje w tle mogą nie być możliwe dla wszystkich aplikacji.",
|
||||
"backgroundUpdateLimitsExplanation": "Powodzenie instalacji w tle można określić dopiero po otwarciu Obtainium.",
|
||||
"verifyLatestTag": "Zweryfikuj najnowszy tag",
|
||||
"intermediateLinkRegex": "Filtr linków \"pośrednich\" do odwiedzenia w pierwszej kolejności",
|
||||
"intermediateLinkNotFound": "Nie znaleziono linku pośredniego"
|
||||
"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": "Automatycznie wybierz najwyższy kod wersji APK",
|
||||
"autoSelectHighestVersionCode": "Automatycznie wybierz najwyższy kod wersji APK",
|
||||
"versionExtractionRegEx": "Wyrażenie regularne wyodrębniające wersję",
|
||||
"matchGroupToUse": "Dopasuj grupę do użycia",
|
||||
"highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
|
||||
@ -276,7 +273,13 @@
|
||||
"one": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} minutę",
|
||||
"few": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} minuty",
|
||||
"many": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} minut",
|
||||
"other": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} minuty"
|
||||
"other": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} minuty"
|
||||
},
|
||||
"bgUpdateGotErrorRetryInMinutes": {
|
||||
"one": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minutę",
|
||||
"few": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minuty",
|
||||
"many": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minut",
|
||||
"other": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minuty"
|
||||
},
|
||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||
"one": "W tle znaleziono {} aktualizację - w razie potrzeby użytkownik zostanie o tym powiadomiony",
|
||||
@ -337,8 +340,4 @@
|
||||
"few": "{} i {} inne apki mogły zostać zaktualizowane.",
|
||||
"many": "{} i {} innych apek mogło zostać zaktualizowanych.",
|
||||
"other": "{} i {} inne apki mogły zostać zaktualizowane."
|
||||
},
|
||||
"enableBackgroundUpdates": "Włącz aktualizacje w tle",
|
||||
"intermediateLinkRegex": "Filtr linków \"pośrednich\" do odwiedzenia w pierwszej kolejności",
|
||||
"intermediateLinkNotFound": "Nie znaleziono linku pośredniego"
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ class FDroid extends AppSource {
|
||||
FDroid() {
|
||||
host = 'f-droid.org';
|
||||
name = tr('fdroid');
|
||||
naiveStandardVersionDetection = true;
|
||||
canSearch = true;
|
||||
additionalSourceAppSpecificSettingFormItems = [
|
||||
[
|
||||
|
@ -35,7 +35,7 @@ class Uptodown extends AppSource {
|
||||
var html = parse(res.body);
|
||||
String? version = html.querySelector('div.version')?.innerHtml;
|
||||
String? apkUrl =
|
||||
html.querySelector('#detail-download-button')?.attributes['data-url'];
|
||||
'${standardUrl.split('/').reversed.toList().sublist(1).reversed.join('/')}/post-download';
|
||||
String? name = html.querySelector('#detail-app-name')?.innerHtml.trim();
|
||||
String? author = html.querySelector('#author-link')?.innerHtml.trim();
|
||||
var detailElements = html.querySelectorAll('#technical-information td');
|
||||
|
@ -16,14 +16,13 @@ class WhatsApp extends AppSource {
|
||||
@override
|
||||
Future<String> apkUrlPrefetchModifier(
|
||||
String apkUrl, String standardUrl) async {
|
||||
Response res = await sourceRequest('https://www.whatsapp.com/android');
|
||||
Response res = await sourceRequest('$standardUrl/android');
|
||||
if (res.statusCode == 200) {
|
||||
var targetLinks = parse(res.body)
|
||||
.querySelectorAll('a')
|
||||
.map((e) => e.attributes['href'] ?? '')
|
||||
.where((e) => e.isNotEmpty)
|
||||
.where((e) =>
|
||||
e.contains('content.whatsapp.net') && e.contains('WhatsApp.apk'))
|
||||
.where((e) => e.contains('WhatsApp.apk'))
|
||||
.toList();
|
||||
if (targetLinks.isEmpty) {
|
||||
throw NoAPKError();
|
||||
@ -39,37 +38,16 @@ class WhatsApp extends AppSource {
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
Response res = await sourceRequest('https://www.whatsapp.com/android');
|
||||
if (res.statusCode == 200) {
|
||||
var targetElements = parse(res.body)
|
||||
.querySelectorAll('p')
|
||||
.where((element) => element.innerHtml.contains('Version '))
|
||||
.toList();
|
||||
if (targetElements.isEmpty) {
|
||||
throw NoVersionError();
|
||||
}
|
||||
var vLines = targetElements[0]
|
||||
.innerHtml
|
||||
.split('\n')
|
||||
.where((element) => element.contains('Version '))
|
||||
.toList();
|
||||
if (vLines.isEmpty) {
|
||||
throw NoVersionError();
|
||||
}
|
||||
var versionMatch = RegExp('[0-9]+(\\.[0-9]+)+').firstMatch(vLines[0]);
|
||||
if (versionMatch == null) {
|
||||
throw NoVersionError();
|
||||
}
|
||||
String version =
|
||||
vLines[0].substring(versionMatch.start, versionMatch.end);
|
||||
return APKDetails(
|
||||
version,
|
||||
getApkUrlsFromUrls([
|
||||
'https://www.whatsapp.com/android?v=$version&=thisIsaPlaceholder&a=realURLPrefetchedAtDownloadTime'
|
||||
]),
|
||||
AppNames('Meta', 'WhatsApp'));
|
||||
} else {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
// This is a CDN link that is consistent per version
|
||||
// But it has query params that change constantly
|
||||
Uri apkUri =
|
||||
Uri.parse(await apkUrlPrefetchModifier(standardUrl, standardUrl));
|
||||
var unusableApkUrl = '${apkUri.origin}/${apkUri.path}';
|
||||
// So we use the param-less URL is a pseudo-version to add the app and check for updates
|
||||
// See #357 for why we can't scrape the version number directly
|
||||
// But we re-fetch the URL again with its latest query params at the actual download time
|
||||
String version = unusableApkUrl.hashCode.toString();
|
||||
return APKDetails(version, getApkUrlsFromUrls([unusableApkUrl]),
|
||||
AppNames('Meta', 'WhatsApp'));
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:android_package_installer/android_package_installer.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -72,6 +74,9 @@ class MultiAppMultiError extends ObtainiumError {
|
||||
MultiAppMultiError() : super(tr('placeholder'), unexpected: true);
|
||||
|
||||
add(String appId, dynamic error, {String? appName}) {
|
||||
if (error is SocketException) {
|
||||
error = error.message;
|
||||
}
|
||||
rawErrors[appId] = error;
|
||||
var string = error.toString();
|
||||
var tempIds = idsByErrorString.remove(string);
|
||||
@ -83,12 +88,17 @@ class MultiAppMultiError extends ObtainiumError {
|
||||
}
|
||||
}
|
||||
|
||||
String errorString(String appId) =>
|
||||
'${appIdNames.containsKey(appId) ? '${appIdNames[appId]} ($appId)' : appId}: ${rawErrors[appId].toString()}';
|
||||
String errorString(String appId, {bool includeIdsWithNames = false}) =>
|
||||
'${appIdNames.containsKey(appId) ? '${appIdNames[appId]}${includeIdsWithNames ? ' ($appId)' : ''}' : appId}: ${rawErrors[appId].toString()}';
|
||||
|
||||
String errorsAppsString(String errString, List<String> appIds,
|
||||
{bool includeIdsWithNames = false}) =>
|
||||
'$errString [${list2FriendlyString(appIds.map((id) => appIdNames.containsKey(id) == true ? '${appIdNames[id]}${includeIdsWithNames ? ' ($id)' : ''}' : id).toList())}]';
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
idsByErrorString.keys.map((e) => errorString(e)).join('\n\n');
|
||||
String toString() => idsByErrorString.entries
|
||||
.map((e) => errorsAppsString(e.key, e.value))
|
||||
.join('\n\n');
|
||||
}
|
||||
|
||||
showError(dynamic e, BuildContext context) {
|
||||
|
@ -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.23';
|
||||
const String currentVersion = '0.14.26';
|
||||
const String currentReleaseTag =
|
||||
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
||||
|
||||
|
@ -1136,7 +1136,6 @@ class AppsProvider with ChangeNotifier {
|
||||
return null;
|
||||
}
|
||||
if (exportDir == null) {
|
||||
logs.add('Skipping auto-export as dir is not set.');
|
||||
return null;
|
||||
}
|
||||
var files = await saf
|
||||
@ -1369,116 +1368,127 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
(<List<MapEntry<String, int>>>[]))
|
||||
];
|
||||
|
||||
bool installMode = toCheck.isEmpty &&
|
||||
toInstall.isNotEmpty; // Task is either in update mode or install mode
|
||||
var netResult = await (Connectivity().checkConnectivity());
|
||||
|
||||
if (netResult == ConnectivityResult.none) {
|
||||
var networkBasedRetryInterval = 15;
|
||||
var nextRegularCheck = appsProvider.settingsProvider.lastBGCheckTime
|
||||
.add(Duration(minutes: appsProvider.settingsProvider.updateInterval));
|
||||
var potentialNetworkRetryCheck =
|
||||
DateTime.now().add(Duration(minutes: networkBasedRetryInterval));
|
||||
var shouldRetry = potentialNetworkRetryCheck.isBefore(nextRegularCheck);
|
||||
logs.add(
|
||||
'BG update task $taskId: No network. Will ${shouldRetry ? 'retry in $networkBasedRetryInterval minutes' : 'not retry'}.');
|
||||
AndroidAlarmManager.oneShot(
|
||||
const Duration(minutes: 15), taskId + 1, bgUpdateCheck,
|
||||
params: {
|
||||
'toCheck': toCheck
|
||||
.map((entry) => {'key': entry.key, 'value': entry.value})
|
||||
.toList(),
|
||||
'toInstall': toInstall
|
||||
.map((entry) => {'key': entry.key, 'value': entry.value})
|
||||
.toList(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var networkRestricted = false;
|
||||
if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) {
|
||||
networkRestricted = (netResult != ConnectivityResult.wifi) &&
|
||||
(netResult != ConnectivityResult.ethernet);
|
||||
}
|
||||
|
||||
bool installMode =
|
||||
toCheck.isEmpty; // Task is either in update mode or install mode
|
||||
|
||||
// In install mode, grab all available silent updates unless explicitly told otherwise
|
||||
if (installMode && toInstall.isEmpty && !networkRestricted) {
|
||||
var temp = appsProvider.findExistingUpdates(installedOnly: true);
|
||||
for (var i = 0; i < temp.length; i++) {
|
||||
if (await appsProvider
|
||||
.canInstallSilently(appsProvider.apps[temp[i]]!.app)) {
|
||||
toInstall.add(MapEntry(temp[i], 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logs.add(
|
||||
'BG ${installMode ? 'install' : 'update'} task $taskId: Started (${installMode ? toInstall.length : toCheck.length}).');
|
||||
|
||||
if (!installMode) {
|
||||
// If in update mode, we check for updates.
|
||||
// We divide the results into 4 groups:
|
||||
// - toNotify - Apps with updates that the user will be notified about (can't be silently installed)
|
||||
// - toRetry - Apps with update check errors that will be retried in a while
|
||||
// - toThrow - Apps with update check errors that the user will be notified about (no retry)
|
||||
// - toInstall - Apps with updates that will be installed silently
|
||||
// After grouping the updates, we take care of toNotify and toThrow first
|
||||
// Then if toRetry is not empty, we schedule another update task to run in a while (toInstall is retained)
|
||||
// If toRetry is empty, we take care of toInstall
|
||||
|
||||
// Init. vars.
|
||||
List<App> updates = [];
|
||||
// If in update mode...
|
||||
var didCompleteChecking = false;
|
||||
CheckingUpdatesNotification? notif;
|
||||
// Loop through all updates and check each
|
||||
List<App> toNotify = [];
|
||||
List<MapEntry<String, int>> toRetry = [];
|
||||
var retryAfterXSeconds = 0;
|
||||
List<String> toThrow = [];
|
||||
var networkRestricted = false;
|
||||
if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) {
|
||||
var netResult = await (Connectivity().checkConnectivity());
|
||||
networkRestricted = (netResult != ConnectivityResult.wifi) &&
|
||||
(netResult != ConnectivityResult.ethernet);
|
||||
}
|
||||
MultiAppMultiError? errors;
|
||||
CheckingUpdatesNotification notif =
|
||||
CheckingUpdatesNotification(plural('apps', toCheck.length));
|
||||
|
||||
try {
|
||||
// Check for updates
|
||||
notificationsProvider.notify(notif, cancelExisting: true);
|
||||
updates = await appsProvider.checkUpdates(
|
||||
specificIds: toCheck.map((e) => e.key).toList());
|
||||
} catch (e) {
|
||||
// If there were errors, group them into toRetry and toThrow
|
||||
if (e is Map) {
|
||||
updates = e['updates'];
|
||||
errors = e['errors'];
|
||||
errors!.rawErrors.forEach((key, err) {
|
||||
logs.add(
|
||||
'BG update task $taskId: Got error on checking for $key \'${err.toString()}\'.');
|
||||
var toCheckApp = toCheck.where((element) => element.key == key).first;
|
||||
if (toCheckApp.value < maxAttempts) {
|
||||
toRetry.add(MapEntry(toCheckApp.key, toCheckApp.value + 1));
|
||||
var minRetryIntervalForThisApp = err is RateLimitError
|
||||
? (err.remainingMinutes * 60)
|
||||
: e is ClientException
|
||||
? (15 * 60)
|
||||
: pow(toCheckApp.value + 1, 2).toInt();
|
||||
if (minRetryIntervalForThisApp > retryAfterXSeconds) {
|
||||
retryAfterXSeconds = minRetryIntervalForThisApp;
|
||||
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))) {
|
||||
toNotify.add(newApp);
|
||||
}
|
||||
}
|
||||
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 {
|
||||
// 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);
|
||||
}
|
||||
} else {
|
||||
toThrow.add(key);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// We don't expect to ever get here in any situation so no need to catch
|
||||
logs.add('Fatal error in BG update task: ${e.toString()}');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
notificationsProvider.cancel(notif.id);
|
||||
}
|
||||
|
||||
// Group the updates into toNotify and toInstall
|
||||
for (var i = 0; i < updates.length; i++) {
|
||||
if (networkRestricted ||
|
||||
!(await appsProvider.canInstallSilently(updates[i]))) {
|
||||
toNotify.add(updates[i]);
|
||||
} else {
|
||||
toInstall.add(MapEntry(updates[i].id, 0));
|
||||
if (toNotify.isNotEmpty) {
|
||||
notificationsProvider.notify(UpdateNotification(toNotify));
|
||||
}
|
||||
}
|
||||
|
||||
// Send the update notification
|
||||
if (toNotify.isNotEmpty) {
|
||||
notificationsProvider.notify(UpdateNotification(toNotify));
|
||||
}
|
||||
|
||||
// Send the error notifications
|
||||
if (toThrow.isNotEmpty) {
|
||||
for (var appId in toThrow) {
|
||||
notificationsProvider.notify(ErrorCheckingUpdatesNotification(
|
||||
errors!.errorString(appId),
|
||||
id: Random().nextInt(10000)));
|
||||
}
|
||||
}
|
||||
|
||||
// if there are update checks to retry, schedule a retry task
|
||||
if (toRetry.isNotEmpty) {
|
||||
logs.add(
|
||||
'BG update task $taskId: Will retry in $retryAfterXSeconds seconds.');
|
||||
AndroidAlarmManager.oneShot(
|
||||
Duration(seconds: retryAfterXSeconds), taskId + 1, bgUpdateCheck,
|
||||
params: {
|
||||
'toCheck': toRetry
|
||||
.map((entry) => {'key': entry.key, 'value': entry.value})
|
||||
.toList(),
|
||||
'toInstall': toInstall
|
||||
.map((entry) => {'key': entry.key, 'value': entry.value})
|
||||
.toList(),
|
||||
});
|
||||
} else if (toInstall.isNotEmpty) {
|
||||
// If there are no more update checks, schedule an install task
|
||||
// If you're done checking and found some silently installable updates, schedule another task which will run in install mode
|
||||
if (didCompleteChecking) {
|
||||
logs.add(
|
||||
'BG update task $taskId: Done. Scheduling install task to run immediately.');
|
||||
AndroidAlarmManager.oneShot(
|
||||
@ -1489,14 +1499,11 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
||||
.map((entry) => {'key': entry.key, 'value': entry.value})
|
||||
.toList()
|
||||
});
|
||||
} else {
|
||||
logs.add('BG install task $taskId: Done.');
|
||||
} else if (didCompleteChecking) {
|
||||
logs.add('BG update task $taskId: Done.');
|
||||
}
|
||||
}
|
||||
|
||||
if (installMode) {
|
||||
// If in install mode, we install silent updates.
|
||||
|
||||
} else {
|
||||
// If in install mode...
|
||||
var didCompleteInstalling = false;
|
||||
var tempObtArr = toInstall.where((element) => element.key == obtainiumId);
|
||||
if (tempObtArr.isNotEmpty) {
|
||||
|
@ -28,6 +28,7 @@ import 'package:obtainium/app_sources/steammobile.dart';
|
||||
import 'package:obtainium/app_sources/telegramapp.dart';
|
||||
import 'package:obtainium/app_sources/uptodown.dart';
|
||||
import 'package:obtainium/app_sources/vlc.dart';
|
||||
import 'package:obtainium/app_sources/whatsapp.dart';
|
||||
import 'package:obtainium/components/generated_form.dart';
|
||||
import 'package:obtainium/custom_errors.dart';
|
||||
import 'package:obtainium/mass_app_sources/githubstars.dart';
|
||||
@ -557,7 +558,7 @@ class SourceProvider {
|
||||
Mullvad(),
|
||||
Signal(),
|
||||
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(),
|
||||
SteamMobile(),
|
||||
NeutronCode(),
|
||||
|
28
pubspec.lock
28
pubspec.lock
@ -5,18 +5,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: android_alarm_manager_plus
|
||||
sha256: c20d91a9096596f66274bf8172321c278f9cba8091638f80205fe66d31587fa5
|
||||
sha256: "82fb28c867c4b3dd7e9157728e46426b8916362f977dbba46b949210f00099f4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "3.0.3"
|
||||
android_intent_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: android_intent_plus
|
||||
sha256: f72ae20bb37108694f442e7ae6acbd28b453ca62ce86842f6787b784355abfe6
|
||||
sha256: e1c62bb41c90e15083b7fb84dc327fe90396cc9c1445b55ff1082144fabfb4d9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
version: "4.0.3"
|
||||
android_package_installer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -46,10 +46,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: "06a96f1249f38a00435b3b0c9a3246d934d7dbc8183fc7c9e56989860edb99d4"
|
||||
sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.4"
|
||||
version: "3.4.6"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -118,10 +118,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: connectivity_plus
|
||||
sha256: "77a180d6938f78ca7d2382d2240eb626c0f6a735d0bfdce227d8ffb80f95c48b"
|
||||
sha256: "94d51c6f1299133a2baa4c5c3d2c11ec7d7fb4768dee5c52a56f7d7522fcf70e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
version: "5.0.0"
|
||||
connectivity_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -562,10 +562,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: f2343e9fa9c22ae4fd92d4732755bfe452214e7189afcc097380950cf567b4b2
|
||||
sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.11.5"
|
||||
version: "3.12.0"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -586,10 +586,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
|
||||
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
version: "3.1.3"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -634,10 +634,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: b7f41bad7e521d205998772545de63ff4e6c97714775902c199353f8bf1511ac
|
||||
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.2.2"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
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
|
||||
# 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.23+215 # When changing this, update the tag in main() accordingly
|
||||
version: 0.14.26+218 # When changing this, update the tag in main() accordingly
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
@ -64,7 +64,7 @@ dependencies:
|
||||
flutter_markdown: ^0.6.14
|
||||
flutter_archive: ^5.0.0
|
||||
hsluv: ^1.1.3
|
||||
connectivity_plus: ^4.0.2
|
||||
connectivity_plus: ^5.0.0
|
||||
shared_storage: ^0.8.0
|
||||
|
||||
dev_dependencies:
|
||||
|
Reference in New Issue
Block a user