mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-13 21:36:42 +02:00
Compare commits
76 Commits
v0.14.19-b
...
v0.14.24-b
Author | SHA1 | Date | |
---|---|---|---|
02f374fdf0 | |||
3536dcd775 | |||
6fa887e536 | |||
71755763b9 | |||
5aabcccfd4 | |||
1e009e2211 | |||
811bc6434b | |||
a5b2d06742 | |||
2c00674835 | |||
9bbff1f436 | |||
c75416f9be | |||
2cb750a7b0 | |||
abfec51964 | |||
e314717a3e | |||
290a78acb5 | |||
b40a4dd243 | |||
b38f6d0581 | |||
5c562a6bdb | |||
238d43f916 | |||
818dc23089 | |||
ddb50940ce | |||
96fb97f4ef | |||
6c0459c2ae | |||
30a9e1ee4b | |||
15a1f1e6e0 | |||
827e2b161c | |||
e95230859e | |||
95cb8ca493 | |||
5735293efb | |||
035df1f338 | |||
36a8b04c4f | |||
3d5411ef28 | |||
df7b74e727 | |||
0138599120 | |||
66446cbffc | |||
ef40f3900f | |||
ce259764a8 | |||
65187cb17b | |||
8f25245ad1 | |||
58b8baa019 | |||
ba1db88c8d | |||
c8f2f42a35 | |||
4e5a9b2af5 | |||
d1ab961c14 | |||
33caf4644e | |||
f34ffe18a8 | |||
4b1c5e111d | |||
dbd7c296c6 | |||
df93cbde8f | |||
d8cbc121a7 | |||
8163cd5c8f | |||
e9e9adb174 | |||
21fdfc1eef | |||
4304251e1e | |||
ae0cd74b0e | |||
6935bff244 | |||
102b9da617 | |||
8bd0d185ae | |||
7bfc5ae0a8 | |||
c72a41db9d | |||
5fbb1c2e32 | |||
850dd53c69 | |||
e3baf91037 | |||
4c811c9c04 | |||
4bc9f5826e | |||
9f19e4dc83 | |||
c9cb865c9c | |||
b8a7dd984d | |||
3ede46c445 | |||
87c4af94d3 | |||
7e85ccaf28 | |||
db3a262410 | |||
70162da491 | |||
f3632a4033 | |||
2f4e176116 | |||
3ea8dc598c |
70
.github/workflows/release.yml
vendored
Normal file
70
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
name: Build and Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: subosito/flutter-action@v2
|
||||||
|
|
||||||
|
- name: Import GPG key
|
||||||
|
id: import_pgp_key
|
||||||
|
uses: crazy-max/ghaction-import-gpg@v6
|
||||||
|
with:
|
||||||
|
gpg_private_key: ${{ secrets.PGP_KEY_BASE64 }}
|
||||||
|
passphrase: ${{ secrets.PGP_PASSPHRASE }}
|
||||||
|
|
||||||
|
- name: Build APKs
|
||||||
|
run: |
|
||||||
|
sed -i 's/signingConfig signingConfigs.release//g' android/app/build.gradle
|
||||||
|
flutter build apk && flutter build apk --split-per-abi
|
||||||
|
rm ./build/app/outputs/flutter-apk/*.sha1
|
||||||
|
ls -l ./build/app/outputs/flutter-apk/
|
||||||
|
|
||||||
|
- name: Sign APKs
|
||||||
|
env:
|
||||||
|
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
|
||||||
|
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||||
|
PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
|
||||||
|
run: |
|
||||||
|
echo "${KEYSTORE_BASE64}" | base64 -d > apksign.keystore
|
||||||
|
for apk in ./build/app/outputs/flutter-apk/*-release*.apk; do
|
||||||
|
unsignedFn=${apk/-release/-unsigned}
|
||||||
|
mv "$apk" "$unsignedFn"
|
||||||
|
${ANDROID_HOME}/build-tools/30.0.2/apksigner sign --ks apksign.keystore --ks-pass pass:"${KEYSTORE_PASSWORD}" --out "${apk}" "${unsignedFn}"
|
||||||
|
sha256sum ${apk} | cut -d " " -f 1 > "$apk".sha256
|
||||||
|
gpg --batch --pinentry-mode loopback --passphrase "${PGP_PASSPHRASE}" --sign --detach-sig "$apk".sha256
|
||||||
|
done
|
||||||
|
rm apksign.keystore
|
||||||
|
PGP_KEY_FINGERPRINT="${{ steps.import_pgp_key.outputs.fingerprint }}"
|
||||||
|
|
||||||
|
- name: Extract Version
|
||||||
|
id: extract_version
|
||||||
|
run: |
|
||||||
|
VERSION=$(grep -oP "currentVersion = '\K[^']+" lib/main.dart)
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
TAG=$(grep -oP "'.*\\\$currentVersion.*'" lib/main.dart | head -c -2 | tail -c +2 | sed "s/\$currentVersion/$VERSION/g")
|
||||||
|
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||||
|
if [ -n "$(echo $TAG | grep -oP '\-beta$')" ]; then BETA=true; else BETA=false; fi
|
||||||
|
echo "beta=$BETA" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Create Tag
|
||||||
|
uses: mathieudutour/github-tag-action@v6.1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GH_ACCESS_TOKEN }}
|
||||||
|
custom_tag: "${{ steps.extract_version.outputs.tag }}"
|
||||||
|
tag_prefix: ""
|
||||||
|
|
||||||
|
- name: Create Release And Upload APKs
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GH_ACCESS_TOKEN }}
|
||||||
|
tag: "${{ steps.extract_version.outputs.tag }}"
|
||||||
|
prerelease: "${{ steps.extract_version.outputs.beta }}"
|
||||||
|
artifacts: ./build/app/outputs/flutter-apk/*-release*.apk*
|
||||||
|
generateReleaseNotes: true
|
@ -122,15 +122,15 @@
|
|||||||
"ascending": "Aufsteigend",
|
"ascending": "Aufsteigend",
|
||||||
"descending": "Absteigend",
|
"descending": "Absteigend",
|
||||||
"bgUpdateCheckInterval": "Prüfintervall für Hintergrundaktualisierung",
|
"bgUpdateCheckInterval": "Prüfintervall für Hintergrundaktualisierung",
|
||||||
"neverManualOnly": "Nie - nur manuell",
|
"neverManualOnly": "Nie – nur manuell",
|
||||||
"appearance": "Aussehen",
|
"appearance": "Aussehen",
|
||||||
"showWebInAppView": "Quellwebseite in der App-Ansicht anzeigen",
|
"showWebInAppView": "Quellwebseite in der App-Ansicht anzeigen",
|
||||||
"pinUpdates": "Apps mit Aktualisierungen oben anheften",
|
"pinUpdates": "Apps mit Aktualisierungen oben anheften",
|
||||||
"updates": "Aktualisierungen",
|
"updates": "Aktualisierungen",
|
||||||
"sourceSpecific": "Quellenspezifisch",
|
"sourceSpecific": "Quellenspezifisch",
|
||||||
"appSource": "App-Quelle",
|
"appSource": "App-Quelle",
|
||||||
"noLogs": "Keine Protokolle",
|
"noLogs": "Keine Logs",
|
||||||
"appLogs": "App Protokolle",
|
"appLogs": "App-Logs",
|
||||||
"close": "Schließen",
|
"close": "Schließen",
|
||||||
"share": "Teilen",
|
"share": "Teilen",
|
||||||
"appNotFound": "App nicht gefunden",
|
"appNotFound": "App nicht gefunden",
|
||||||
@ -256,25 +256,25 @@
|
|||||||
"filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern",
|
"filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern",
|
||||||
"trySelectingSuggestedVersionCode": "Versuchen, die vorgeschlagene APK-Code-Version auszuwählen",
|
"trySelectingSuggestedVersionCode": "Versuchen, die vorgeschlagene APK-Code-Version auszuwählen",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Retain release order from API",
|
||||||
"reverseSort": "Reverse sorting",
|
"reverseSort": "Umgekehrtes Sortieren",
|
||||||
"debugMenu": "Debug Menu",
|
"debugMenu": "Debug Menü",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"bgTaskStarted": "Hintergrundaufgabe gestartet – Logs prüfen.",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "App entfernen?",
|
"one": "App entfernen?",
|
||||||
"other": "Apps entfernen?"
|
"other": "Apps entfernen?"
|
||||||
},
|
},
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut",
|
"one": "Zu viele Anfragen (Rate begrenzt) – versuchen Sie es in {} Minute erneut",
|
||||||
"other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten erneut"
|
"other": "Zu viele Anfragen (Rate begrenzt) – versuchen Sie es in {} Minuten erneut"
|
||||||
},
|
},
|
||||||
"bgUpdateGotErrorRetryInMinutes": {
|
"bgUpdateGotErrorRetryInMinutes": {
|
||||||
"one": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minute geplant",
|
"one": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minute geplant",
|
||||||
"other": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minuten geplant"
|
"other": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minuten geplant"
|
||||||
},
|
},
|
||||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||||
"one": "Hintergrundaktualisierungsprüfung fand {} Aktualisierung - benachrichtigt den Benutzer, falls erforderlich",
|
"one": "Die Hintergrundaktualisierungsprüfung fand {} Aktualisierung – benachrichtigt den Benutzer, falls erforderlich",
|
||||||
"other": "Hintergrundaktualisierungsprüfung fand {} Aktualisierungen - benachrichtigt den Benutzer, falls erforderlich"
|
"other": "Die Hintergrundaktualisierungsprüfung fand {} Aktualisierungen – benachrichtigt den Benutzer, falls erforderlich"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"one": "{} App",
|
"one": "{} App",
|
||||||
@ -297,8 +297,8 @@
|
|||||||
"other": "{} Tage"
|
"other": "{} Tage"
|
||||||
},
|
},
|
||||||
"clearedNLogsBeforeXAfterY": {
|
"clearedNLogsBeforeXAfterY": {
|
||||||
"one": "{n} Protokoll gelöscht (vorher = {vorher}, nachher = {nachher})",
|
"one": "{n} Log gelöscht (vorher = {vorher}, nachher = {nachher})",
|
||||||
"other": "{n} Protokolle gelöscht (vorher = {vorher}, nachher = {nachher})"
|
"other": "{n} Logs gelöscht (vorher = {vorher}, nachher = {nachher})"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesAvailable": {
|
"xAndNMoreUpdatesAvailable": {
|
||||||
"one": "{} und 1 weitere App haben Aktualisierungen.",
|
"one": "{} und 1 weitere App haben Aktualisierungen.",
|
||||||
|
@ -34,10 +34,10 @@
|
|||||||
"githubStarredRepos": "Repozytoria GitHub oznaczone gwiazdką",
|
"githubStarredRepos": "Repozytoria GitHub oznaczone gwiazdką",
|
||||||
"uname": "Nazwa użytkownika",
|
"uname": "Nazwa użytkownika",
|
||||||
"wrongArgNum": "Nieprawidłowa liczba podanych argumentów",
|
"wrongArgNum": "Nieprawidłowa liczba podanych argumentów",
|
||||||
"xIsTrackOnly": "{} jest tylko obserwowana",
|
"xIsTrackOnly": "{} jest tylko obserwowane",
|
||||||
"source": "Źródło",
|
"source": "Źródło",
|
||||||
"app": "Aplikacja",
|
"app": "Aplikacja",
|
||||||
"appsFromSourceAreTrackOnly": "Aplikacje z tego źródła są „Obserwowane”.",
|
"appsFromSourceAreTrackOnly": "Aplikacje z tego źródła są „tylko obserwowane”.",
|
||||||
"youPickedTrackOnly": "Wybrano opcję „Tylko obserwuj”.",
|
"youPickedTrackOnly": "Wybrano opcję „Tylko obserwuj”.",
|
||||||
"trackOnlyAppDescription": "Aplikacja będzie obserwowana pod kątem aktualizacji, ale Obtainium nie będzie w stanie jej pobrać ani zainstalować.",
|
"trackOnlyAppDescription": "Aplikacja będzie obserwowana pod kątem aktualizacji, ale Obtainium nie będzie w stanie jej pobrać ani zainstalować.",
|
||||||
"cancelled": "Anulowano",
|
"cancelled": "Anulowano",
|
||||||
@ -52,7 +52,7 @@
|
|||||||
"additionalOptsFor": "Dodatkowe opcje dla {}",
|
"additionalOptsFor": "Dodatkowe opcje dla {}",
|
||||||
"supportedSources": "Obsługiwane źródła",
|
"supportedSources": "Obsługiwane źródła",
|
||||||
"trackOnlyInBrackets": "(tylko obserwowane)",
|
"trackOnlyInBrackets": "(tylko obserwowane)",
|
||||||
"searchableInBrackets": "(Wyszukiwalne)",
|
"searchableInBrackets": "(wyszukiwalne)",
|
||||||
"appsString": "Aplikacje",
|
"appsString": "Aplikacje",
|
||||||
"noApps": "Brak aplikacji",
|
"noApps": "Brak aplikacji",
|
||||||
"noAppsForFilter": "Brak aplikacji dla filtra",
|
"noAppsForFilter": "Brak aplikacji dla filtra",
|
||||||
@ -70,7 +70,7 @@
|
|||||||
"removeSelectedApps": "Usuń wybrane aplikacje",
|
"removeSelectedApps": "Usuń wybrane aplikacje",
|
||||||
"updateX": "Zaktualizuj {}",
|
"updateX": "Zaktualizuj {}",
|
||||||
"installX": "Zainstaluj {}",
|
"installX": "Zainstaluj {}",
|
||||||
"markXTrackOnlyAsUpdated": "Oznacz {}\n(Tylko obserwowana)\njako zaktualizowaną",
|
"markXTrackOnlyAsUpdated": "Oznacz {}\n(tylko obserwowana)\njako zaktualizowaną",
|
||||||
"changeX": "Zmień {}",
|
"changeX": "Zmień {}",
|
||||||
"installUpdateApps": "Instaluj/aktualizuj aplikacje",
|
"installUpdateApps": "Instaluj/aktualizuj aplikacje",
|
||||||
"installUpdateSelectedApps": "Zainstaluj/zaktualizuj wybrane aplikacje",
|
"installUpdateSelectedApps": "Zainstaluj/zaktualizuj wybrane aplikacje",
|
||||||
@ -261,11 +261,11 @@
|
|||||||
"autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
|
"autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
|
||||||
"filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego",
|
"filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego",
|
||||||
"trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
|
"trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API",
|
||||||
"reverseSort": "Reverse sorting",
|
"reverseSort": "Odwrotne sortowanie",
|
||||||
"debugMenu": "Debug Menu",
|
"debugMenu": "Menu debugowania",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"bgTaskStarted": "Uruchomiono zadanie w tle - sprawdź logi.",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"runBgCheckNow": "Wymuś sprawdzenie aktualizacji w tle",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Usunąć aplikację?",
|
"one": "Usunąć aplikację?",
|
||||||
"few": "Usunąć aplikacje?",
|
"few": "Usunąć aplikacje?",
|
||||||
|
@ -128,7 +128,7 @@
|
|||||||
"pinUpdates": "Fixar atualizações no topo da visão de Apps",
|
"pinUpdates": "Fixar atualizações no topo da visão de Apps",
|
||||||
"updates": "Atualizações",
|
"updates": "Atualizações",
|
||||||
"sourceSpecific": "Específico a fonte",
|
"sourceSpecific": "Específico a fonte",
|
||||||
"appSource": "Fonte de Apps",
|
"appSource": "Fonte do App",
|
||||||
"noLogs": "Sem Logs",
|
"noLogs": "Sem Logs",
|
||||||
"appLogs": "Logs do App",
|
"appLogs": "Logs do App",
|
||||||
"close": "Fechar",
|
"close": "Fechar",
|
||||||
@ -250,19 +250,19 @@
|
|||||||
"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",
|
"autoSelectHighestVersionCode": "Auto-selecionar o maior codigo de versão",
|
||||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
"versionExtractionRegEx": "RegEx para Extração de Versão",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"matchGroupToUse": "Grupo de Seleção para Usar",
|
||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"highlightTouchTargets": "Destaque areas de toque menos óbvias",
|
||||||
"pickExportDir": "Pick Export Directory",
|
"pickExportDir": "Escolher Diretorio de Exportação",
|
||||||
"autoExportOnChanges": "Auto-export on changes",
|
"autoExportOnChanges": "Auto-exportar em mudanças",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"filterVersionsByRegEx": "Filtrar Versões por Expressão Regular",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Reter a ordem de lançamento da API",
|
||||||
"reverseSort": "Reverse sorting",
|
"reverseSort": "Ordenação reversa",
|
||||||
"debugMenu": "Debug Menu",
|
"debugMenu": "Menu Debug",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"bgTaskStarted": "Tarefa em segundo plano iniciada - verifique os logs.",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"runBgCheckNow": "Execute a verificação de atualização em segundo plano agora",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remover App?",
|
"one": "Remover App?",
|
||||||
"other": "Remover Apps?"
|
"other": "Remover Apps?"
|
||||||
|
@ -236,30 +236,33 @@
|
|||||||
"addInfoInSettings": "Добавьте эту информацию в Настройки.",
|
"addInfoInSettings": "Добавьте эту информацию в Настройки.",
|
||||||
"githubSourceNote": "Лимит запросов GitHub можно обойти, используя ключ API.",
|
"githubSourceNote": "Лимит запросов GitHub можно обойти, используя ключ API.",
|
||||||
"gitlabSourceNote": "Извлечение APK из GitLab может не работать без ключа API.",
|
"gitlabSourceNote": "Извлечение APK из GitLab может не работать без ключа API.",
|
||||||
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
|
"sortByFileNamesNotLinks": "Сортировать по именам файлов, а не по полным ссылкам",
|
||||||
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
|
"filterReleaseNotesByRegEx": "Фильтровать примечания к выпуску по регулярному выражению",
|
||||||
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
|
"customLinkFilterRegex": "Пользовательский фильтр ссылок APK по регулярному выражению (по умолчанию '.apk$')",
|
||||||
"appsPossiblyUpdated": "App Updates Attempted",
|
"appsPossiblyUpdated": "Попытки обновления приложений",
|
||||||
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
|
"appsPossiblyUpdatedNotifDescription": "Уведомляет пользователя о возможных обновлениях одного или нескольких приложений в фоновом режиме",
|
||||||
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
|
"xWasPossiblyUpdatedToY": "{} возможно был обновлен до {}.",
|
||||||
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
|
"enableBackgroundUpdates": "Включить обновления в фоне",
|
||||||
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
|
"backgroundUpdateReqsExplanation": "Фоновые обновления могут быть невозможны для всех приложений.",
|
||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"backgroundUpdateLimitsExplanation": "Успех фоновой установки можно определить только после открытия Obtainium.",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"verifyLatestTag": "Проверьте тег 'последний'",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"intermediateLinkRegex": "Фильтр ссылок 'промежуточного' типа для приоритетного посещения",
|
||||||
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
"intermediateLinkNotFound": "Промежуточная ссылка не найдена",
|
||||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
"exemptFromBackgroundUpdates": "Исключить из фоновых обновлений (если включено)",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"bgUpdatesOnWiFiOnly": "Отключить фоновые обновления, если нет соединения с Wi-Fi",
|
||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"autoSelectHighestVersionCode": "Автоматически выбирать APK с наивысшим кодом версии",
|
||||||
"pickExportDir": "Pick Export Directory",
|
"versionExtractionRegEx": "Регулярное выражение для извлечения версии",
|
||||||
"autoExportOnChanges": "Auto-export on changes",
|
"matchGroupToUse": "Выберите группу для использования",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"highlightTouchTargets": "Выделить менее очевидные элементы управления касанием",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"pickExportDir": "Выбрать каталог для экспорта",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"autoExportOnChanges": "Автоэкспорт при изменениях",
|
||||||
"reverseSort": "Reverse sorting",
|
"filterVersionsByRegEx": "Фильтровать версии по регулярному выражению",
|
||||||
"debugMenu": "Debug Menu",
|
"trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"dontSortReleasesList": "Сохранить порядок выпусков от API",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"reverseSort": "Обратная сортировка",
|
||||||
|
"debugMenu": "Меню Отладки",
|
||||||
|
"bgTaskStarted": "Фоновая задача начата - проверьте журналы.",
|
||||||
|
"runBgCheckNow": "Запустить проверку фонового обновления сейчас",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Удалить приложение?",
|
"one": "Удалить приложение?",
|
||||||
"other": "Удалить приложения?"
|
"other": "Удалить приложения?"
|
||||||
@ -305,11 +308,11 @@
|
|||||||
"other": "У {} и ещё {} приложений есть обновления."
|
"other": "У {} и ещё {} приложений есть обновления."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesInstalled": {
|
"xAndNMoreUpdatesInstalled": {
|
||||||
"one": "{} и еще 1 приложение были обновлены.",
|
"one": "{} и ещё 1 приложение были обновлены.",
|
||||||
"other": "{} и еще {} приложений были обновлены."
|
"other": "{} и ещё {} приложений были обновлены."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
"one": "{} and 1 more app may have been updated.",
|
"one": "{} и ещё 1 приложение могли быть обновлены.",
|
||||||
"other": "{} and {} more apps may have been updated."
|
"other": "{} и ещё {} приложений могли быть обновлены."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,31 +238,31 @@
|
|||||||
"gitlabSourceNote": "未使用访问令牌时可能无法从 GitLab 获取 APK 文件。",
|
"gitlabSourceNote": "未使用访问令牌时可能无法从 GitLab 获取 APK 文件。",
|
||||||
"sortByFileNamesNotLinks": "使用文件名代替链接进行排序",
|
"sortByFileNamesNotLinks": "使用文件名代替链接进行排序",
|
||||||
"filterReleaseNotesByRegEx": "使用正则表达式筛选发行说明",
|
"filterReleaseNotesByRegEx": "使用正则表达式筛选发行说明",
|
||||||
"customLinkFilterRegex": "使用正则表达式自定义链接筛选(默认模式为“.apk$”)",
|
"customLinkFilterRegex": "使用正则表达式筛选自定义来源 APK 文件链接\n(未填写时,默认匹配模式为“.apk$”)",
|
||||||
"appsPossiblyUpdated": "已尝试更新应用",
|
"appsPossiblyUpdated": "已尝试更新应用",
|
||||||
"appsPossiblyUpdatedNotifDescription": "当应用已尝试在后台更新时发送通知",
|
"appsPossiblyUpdatedNotifDescription": "当应用已尝试在后台更新时发送通知",
|
||||||
"xWasPossiblyUpdatedToY": "已尝试将 {} 更新至 {}。",
|
"xWasPossiblyUpdatedToY": "已尝试将“{}”更新至 {}。",
|
||||||
"enableBackgroundUpdates": "启用后台更新",
|
"enableBackgroundUpdates": "启用后台更新",
|
||||||
"backgroundUpdateReqsExplanation": "后台更新未必适用于所有的应用。",
|
"backgroundUpdateReqsExplanation": "后台更新未必适用于所有的应用。",
|
||||||
"backgroundUpdateLimitsExplanation": "只有在启动 Obtainium 时才能确认安装是否成功。",
|
"backgroundUpdateLimitsExplanation": "只有在启动 Obtainium 时才能确认安装是否成功。",
|
||||||
"verifyLatestTag": "验证“Latest”标签",
|
"verifyLatestTag": "验证“Latest”标签",
|
||||||
"intermediateLinkRegex": "首先访问“中间”链接的过滤器",
|
"intermediateLinkRegex": "筛选一个首先访问的“中转”链接(正则表达式)",
|
||||||
"intermediateLinkNotFound": "中间链接未找到",
|
"intermediateLinkNotFound": "未找到“中转”链接",
|
||||||
"exemptFromBackgroundUpdates": "禁用后台更新(如果全局设置启用)",
|
"exemptFromBackgroundUpdates": "单独禁用后台更新(若已经全局启用)",
|
||||||
"bgUpdatesOnWiFiOnly": "不在连接 WiFi 时禁用后台更新",
|
"bgUpdatesOnWiFiOnly": "未连接 Wi-Fi 时禁用后台更新",
|
||||||
"autoSelectHighestVersionCode": "自动选择最高版本号 APK",
|
"autoSelectHighestVersionCode": "自动选择版本号最高的 APK 文件",
|
||||||
"versionExtractionRegEx": "版本提取正则表达式",
|
"versionExtractionRegEx": "获取版本号的正则表达式",
|
||||||
"matchGroupToUse": "匹配要使用的组",
|
"matchGroupToUse": "引用的捕获组",
|
||||||
"highlightTouchTargets": "突出显示不明显的触摸目标",
|
"highlightTouchTargets": "突出展示不明显的触摸区域",
|
||||||
"pickExportDir": "选择导出目录",
|
"pickExportDir": "选择导出文件夹",
|
||||||
"autoExportOnChanges": "修改时自动导出",
|
"autoExportOnChanges": "数据变更时自动导出",
|
||||||
"filterVersionsByRegEx": "使用正则表达式筛选版本",
|
"filterVersionsByRegEx": "使用正则表达式筛选版本号",
|
||||||
"trySelectingSuggestedVersionCode": "尝试选择推荐版本 APK",
|
"trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "保持来自 API 的发行顺序",
|
||||||
"reverseSort": "Reverse sorting",
|
"reverseSort": "反转排序",
|
||||||
"debugMenu": "Debug Menu",
|
"debugMenu": "调试选项",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"bgTaskStarted": "后台任务已启动 - 详见日志",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"runBgCheckNow": "立即进行后台更新检查",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "是否删除应用?",
|
"one": "是否删除应用?",
|
||||||
"other": "是否删除应用?"
|
"other": "是否删除应用?"
|
||||||
|
@ -22,16 +22,19 @@ class APKPure extends AppSource {
|
|||||||
APKPure() {
|
APKPure() {
|
||||||
host = 'apkpure.com';
|
host = 'apkpure.com';
|
||||||
allowSubDomains = true;
|
allowSubDomains = true;
|
||||||
|
naiveStandardVersionDetection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegExB = RegExp('^https?://m.$host/+[^/]+/+[^/]+');
|
RegExp standardUrlRegExB =
|
||||||
|
RegExp('^https?://m.$host/+[^/]+/+[^/]+(/+[^/]+)?');
|
||||||
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
url = 'https://$host${Uri.parse(url).path}';
|
url = 'https://$host${Uri.parse(url).path}';
|
||||||
}
|
}
|
||||||
RegExp standardUrlRegExA = RegExp('^https?://$host/+[^/]+/+[^/]+');
|
RegExp standardUrlRegExA =
|
||||||
|
RegExp('^https?://$host/+[^/]+/+[^/]+(/+[^/]+)?');
|
||||||
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@ -9,6 +9,7 @@ class Aptoide extends AppSource {
|
|||||||
host = 'aptoide.com';
|
host = 'aptoide.com';
|
||||||
name = tr('Aptoide');
|
name = tr('Aptoide');
|
||||||
allowSubDomains = true;
|
allowSubDomains = true;
|
||||||
|
naiveStandardVersionDetection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -11,6 +11,7 @@ class FDroid extends AppSource {
|
|||||||
FDroid() {
|
FDroid() {
|
||||||
host = 'f-droid.org';
|
host = 'f-droid.org';
|
||||||
name = tr('fdroid');
|
name = tr('fdroid');
|
||||||
|
naiveStandardVersionDetection = true;
|
||||||
canSearch = true;
|
canSearch = true;
|
||||||
additionalSourceAppSpecificSettingFormItems = [
|
additionalSourceAppSpecificSettingFormItems = [
|
||||||
[
|
[
|
||||||
|
@ -120,14 +120,14 @@ class HTML extends AppSource {
|
|||||||
GeneratedFormTextField('matchGroupToUse',
|
GeneratedFormTextField('matchGroupToUse',
|
||||||
label: tr('matchGroupToUse'),
|
label: tr('matchGroupToUse'),
|
||||||
required: false,
|
required: false,
|
||||||
hint: '1',
|
hint: '0',
|
||||||
textInputType: const TextInputType.numberWithOptions(),
|
textInputType: const TextInputType.numberWithOptions(),
|
||||||
additionalValidators: [
|
additionalValidators: [
|
||||||
(value) {
|
(value) {
|
||||||
if (value?.isEmpty == true) {
|
if (value?.isEmpty == true) {
|
||||||
value = null;
|
value = null;
|
||||||
}
|
}
|
||||||
value ??= '1';
|
value ??= '0';
|
||||||
return intValidator(value);
|
return intValidator(value);
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
@ -216,8 +216,12 @@ class HTML extends AppSource {
|
|||||||
if (match.isEmpty) {
|
if (match.isEmpty) {
|
||||||
throw NoVersionError();
|
throw NoVersionError();
|
||||||
}
|
}
|
||||||
version = match.last
|
String matchGroupString =
|
||||||
.group(int.parse(additionalSettings['matchGroupToUse'] as String));
|
(additionalSettings['matchGroupToUse'] as String).trim();
|
||||||
|
if (matchGroupString.isEmpty) {
|
||||||
|
matchGroupString = "0";
|
||||||
|
}
|
||||||
|
version = match.last.group(int.parse(matchGroupString));
|
||||||
if (version?.isEmpty == true) {
|
if (version?.isEmpty == true) {
|
||||||
throw NoVersionError();
|
throw NoVersionError();
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ class Uptodown extends AppSource {
|
|||||||
Uptodown() {
|
Uptodown() {
|
||||||
host = 'uptodown.com';
|
host = 'uptodown.com';
|
||||||
allowSubDomains = true;
|
allowSubDomains = true;
|
||||||
|
naiveStandardVersionDetection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -79,4 +80,20 @@ class Uptodown extends AppSource {
|
|||||||
version, getApkUrlsFromUrls([apkUrl]), AppNames(author, appName),
|
version, getApkUrlsFromUrls([apkUrl]), AppNames(author, appName),
|
||||||
releaseDate: relDate);
|
releaseDate: relDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> apkUrlPrefetchModifier(
|
||||||
|
String apkUrl, String standardUrl) async {
|
||||||
|
var res = await sourceRequest(apkUrl);
|
||||||
|
if (res.statusCode != 200) {
|
||||||
|
throw getObtainiumHttpError(res);
|
||||||
|
}
|
||||||
|
var html = parse(res.body);
|
||||||
|
var finalUrl =
|
||||||
|
(html.querySelector('.post-download')?.attributes['data-url']);
|
||||||
|
if (finalUrl == null) {
|
||||||
|
throw NoAPKError();
|
||||||
|
}
|
||||||
|
return finalUrl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,14 +16,13 @@ class WhatsApp extends AppSource {
|
|||||||
@override
|
@override
|
||||||
Future<String> apkUrlPrefetchModifier(
|
Future<String> apkUrlPrefetchModifier(
|
||||||
String apkUrl, String standardUrl) async {
|
String apkUrl, String standardUrl) async {
|
||||||
Response res = await sourceRequest('https://www.whatsapp.com/android');
|
Response res = await sourceRequest('$standardUrl/android');
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var targetLinks = parse(res.body)
|
var targetLinks = parse(res.body)
|
||||||
.querySelectorAll('a')
|
.querySelectorAll('a')
|
||||||
.map((e) => e.attributes['href'] ?? '')
|
.map((e) => e.attributes['href'] ?? '')
|
||||||
.where((e) => e.isNotEmpty)
|
.where((e) => e.isNotEmpty)
|
||||||
.where((e) =>
|
.where((e) => e.contains('WhatsApp.apk'))
|
||||||
e.contains('content.whatsapp.net') && e.contains('WhatsApp.apk'))
|
|
||||||
.toList();
|
.toList();
|
||||||
if (targetLinks.isEmpty) {
|
if (targetLinks.isEmpty) {
|
||||||
throw NoAPKError();
|
throw NoAPKError();
|
||||||
@ -39,37 +38,16 @@ class WhatsApp extends AppSource {
|
|||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
Response res = await sourceRequest('https://www.whatsapp.com/android');
|
// This is a CDN link that is consistent per version
|
||||||
if (res.statusCode == 200) {
|
// But it has query params that change constantly
|
||||||
var targetElements = parse(res.body)
|
Uri apkUri =
|
||||||
.querySelectorAll('p')
|
Uri.parse(await apkUrlPrefetchModifier(standardUrl, standardUrl));
|
||||||
.where((element) => element.innerHtml.contains('Version '))
|
var unusableApkUrl = '${apkUri.origin}/${apkUri.path}';
|
||||||
.toList();
|
// So we use the param-less URL is a pseudo-version to add the app and check for updates
|
||||||
if (targetElements.isEmpty) {
|
// See #357 for why we can't scrape the version number directly
|
||||||
throw NoVersionError();
|
// But we re-fetch the URL again with its latest query params at the actual download time
|
||||||
}
|
String version = unusableApkUrl.hashCode.toString();
|
||||||
var vLines = targetElements[0]
|
return APKDetails(version, getApkUrlsFromUrls([unusableApkUrl]),
|
||||||
.innerHtml
|
AppNames('Meta', 'WhatsApp'));
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,25 +65,30 @@ class NotImplementedError extends ObtainiumError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MultiAppMultiError extends ObtainiumError {
|
class MultiAppMultiError extends ObtainiumError {
|
||||||
Map<String, List<String>> content = {};
|
Map<String, dynamic> rawErrors = {};
|
||||||
|
Map<String, List<String>> idsByErrorString = {};
|
||||||
|
Map<String, String> appIdNames = {};
|
||||||
|
|
||||||
MultiAppMultiError() : super(tr('placeholder'), unexpected: true);
|
MultiAppMultiError() : super(tr('placeholder'), unexpected: true);
|
||||||
|
|
||||||
add(String appId, String string) {
|
add(String appId, dynamic error, {String? appName}) {
|
||||||
var tempIds = content.remove(string);
|
rawErrors[appId] = error;
|
||||||
|
var string = error.toString();
|
||||||
|
var tempIds = idsByErrorString.remove(string);
|
||||||
tempIds ??= [];
|
tempIds ??= [];
|
||||||
tempIds.add(appId);
|
tempIds.add(appId);
|
||||||
content.putIfAbsent(string, () => tempIds!);
|
idsByErrorString.putIfAbsent(string, () => tempIds!);
|
||||||
|
if (appName != null) {
|
||||||
|
appIdNames[appId] = appName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String errorString(String appId) =>
|
||||||
|
'${appIdNames.containsKey(appId) ? '${appIdNames[appId]} ($appId)' : appId}: ${rawErrors[appId].toString()}';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() =>
|
||||||
String finalString = '';
|
idsByErrorString.keys.map((e) => errorString(e)).join('\n\n');
|
||||||
for (var e in content.keys) {
|
|
||||||
finalString += '$e: ${content[e].toString()}\n\n';
|
|
||||||
}
|
|
||||||
return finalString;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showError(dynamic e, BuildContext context) {
|
showError(dynamic e, BuildContext context) {
|
||||||
|
@ -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.19';
|
const String currentVersion = '0.14.24';
|
||||||
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
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
refreshingSince = DateTime.now();
|
refreshingSince = DateTime.now();
|
||||||
});
|
});
|
||||||
return appsProvider.checkUpdates().catchError((e) {
|
return appsProvider.checkUpdates().catchError((e) {
|
||||||
showError(e, context);
|
showError(e is Map ? e['errors'] : e, context);
|
||||||
return <App>[];
|
return <App>[];
|
||||||
}).whenComplete(() {
|
}).whenComplete(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -833,7 +833,7 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
items: const [],
|
items: const [],
|
||||||
initValid: true,
|
initValid: true,
|
||||||
message: tr('installStatusOfXWillBeResetExplanation',
|
message: tr('installStatusOfXWillBeResetExplanation',
|
||||||
args: [plural('app', selectedAppIds.length)]),
|
args: [plural('apps', selectedAppIds.length)]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
if (values != null) {
|
if (values != null) {
|
||||||
|
@ -102,11 +102,12 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
runObtainiumExport() async {
|
runObtainiumExport({bool pickOnly = false}) async {
|
||||||
HapticFeedback.selectionClick();
|
HapticFeedback.selectionClick();
|
||||||
appsProvider
|
appsProvider
|
||||||
.exportApps(
|
.exportApps(
|
||||||
pickOnly: (await settingsProvider.getExportDir()) == null,
|
pickOnly:
|
||||||
|
pickOnly || (await settingsProvider.getExportDir()) == null,
|
||||||
sp: settingsProvider)
|
sp: settingsProvider)
|
||||||
.then((String? result) {
|
.then((String? result) {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
@ -216,7 +217,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
if (errors.isEmpty) {
|
if (errors.isEmpty) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
showError(
|
showError(
|
||||||
tr('importedX', args: [plural('app', selectedUrls.length)]),
|
tr('importedX',
|
||||||
|
args: [plural('apps', selectedUrls.length)]),
|
||||||
context);
|
context);
|
||||||
} else {
|
} else {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
@ -273,7 +275,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
if (errors.isEmpty) {
|
if (errors.isEmpty) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
showError(
|
showError(
|
||||||
tr('importedX', args: [plural('app', selectedUrls.length)]),
|
tr('importedX', args: [plural('apps', selectedUrls.length)]),
|
||||||
context);
|
context);
|
||||||
} else {
|
} else {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
@ -320,21 +322,38 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
onPressed: appsProvider.apps.isEmpty ||
|
onPressed: appsProvider.apps.isEmpty ||
|
||||||
importInProgress
|
importInProgress
|
||||||
? null
|
? null
|
||||||
: runObtainiumExport,
|
: () {
|
||||||
child: Text(tr(snapshot.data != null
|
runObtainiumExport(pickOnly: true);
|
||||||
? 'obtainiumExport'
|
},
|
||||||
: 'pickExportDir')),
|
child: Text(tr('pickExportDir')),
|
||||||
)),
|
)),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
),
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TextButton(
|
||||||
|
style: outlineButtonStyle,
|
||||||
|
onPressed: appsProvider.apps.isEmpty ||
|
||||||
|
importInProgress ||
|
||||||
|
snapshot.data == null
|
||||||
|
? null
|
||||||
|
: runObtainiumExport,
|
||||||
|
child: Text(tr('obtainiumExport')),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
style: outlineButtonStyle,
|
style: outlineButtonStyle,
|
||||||
onPressed: importInProgress
|
onPressed: importInProgress
|
||||||
? null
|
? null
|
||||||
: runObtainiumImport,
|
: runObtainiumImport,
|
||||||
child: Text(tr('obtainiumImport'))))
|
child: Text(tr('obtainiumImport')))),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (snapshot.data != null)
|
if (snapshot.data != null)
|
||||||
|
@ -449,7 +449,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
logs.add(
|
logs.add(
|
||||||
'Could not install APK from XAPK \'${file.path}\': ${e.toString()}');
|
'Could not install APK from XAPK \'${file.path}\': ${e.toString()}');
|
||||||
errors.add(dir.appId, e.toString());
|
errors.add(dir.appId, e, appName: apps[dir.appId]?.name);
|
||||||
}
|
}
|
||||||
} else if (file.path.toLowerCase().endsWith('.obb')) {
|
} else if (file.path.toLowerCase().endsWith('.obb')) {
|
||||||
await moveObbFile(file, dir.appId);
|
await moveObbFile(file, dir.appId);
|
||||||
@ -457,7 +457,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
if (somethingInstalled) {
|
if (somethingInstalled) {
|
||||||
dir.file.delete(recursive: true);
|
dir.file.delete(recursive: true);
|
||||||
} else if (errors.content.isNotEmpty) {
|
} else if (errors.idsByErrorString.isNotEmpty) {
|
||||||
throw errors;
|
throw errors;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -677,11 +677,11 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
installedIds.add(id);
|
installedIds.add(id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errors.add(id, e.toString());
|
errors.add(id, e, appName: apps[id]?.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors.content.isNotEmpty) {
|
if (errors.idsByErrorString.isNotEmpty) {
|
||||||
throw errors;
|
throw errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -709,14 +709,21 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isVersionDetectionPossible(AppInMemory? app) {
|
bool isVersionDetectionPossible(AppInMemory? app) {
|
||||||
return app?.app.additionalSettings['trackOnly'] != true &&
|
if (app?.app == null) {
|
||||||
app?.app.additionalSettings['versionDetection'] !=
|
return false;
|
||||||
|
}
|
||||||
|
var naiveStandardVersionDetection = SourceProvider()
|
||||||
|
.getSource(app!.app.url, overrideSource: app.app.overrideSource)
|
||||||
|
.naiveStandardVersionDetection;
|
||||||
|
return app.app.additionalSettings['trackOnly'] != true &&
|
||||||
|
app.app.additionalSettings['versionDetection'] !=
|
||||||
'releaseDateAsVersion' &&
|
'releaseDateAsVersion' &&
|
||||||
app?.installedInfo?.versionName != null &&
|
app.installedInfo?.versionName != null &&
|
||||||
app?.app.installedVersion != null &&
|
app.app.installedVersion != null &&
|
||||||
reconcileVersionDifferences(
|
(reconcileVersionDifferences(app.installedInfo!.versionName!,
|
||||||
app!.installedInfo!.versionName!, app.app.installedVersion!) !=
|
app.app.installedVersion!) !=
|
||||||
null;
|
null ||
|
||||||
|
naiveStandardVersionDetection);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given an App and it's on-device info...
|
// Given an App and it's on-device info...
|
||||||
@ -725,8 +732,12 @@ class AppsProvider with ChangeNotifier {
|
|||||||
App app, PackageInfo? installedInfo) {
|
App app, PackageInfo? installedInfo) {
|
||||||
var modded = false;
|
var modded = false;
|
||||||
var trackOnly = app.additionalSettings['trackOnly'] == true;
|
var trackOnly = app.additionalSettings['trackOnly'] == true;
|
||||||
var noVersionDetection = app.additionalSettings['versionDetection'] !=
|
var versionDetectionIsStandard =
|
||||||
'standardVersionDetection';
|
app.additionalSettings['versionDetection'] ==
|
||||||
|
'standardVersionDetection';
|
||||||
|
var naiveStandardVersionDetection = SourceProvider()
|
||||||
|
.getSource(app.url, overrideSource: app.overrideSource)
|
||||||
|
.naiveStandardVersionDetection;
|
||||||
// FIRST, COMPARE THE APP'S REPORTED AND REAL INSTALLED VERSIONS, WHERE ONE IS NULL
|
// FIRST, COMPARE THE APP'S REPORTED AND REAL INSTALLED VERSIONS, WHERE ONE IS NULL
|
||||||
if (installedInfo == null && app.installedVersion != null && !trackOnly) {
|
if (installedInfo == null && app.installedVersion != null && !trackOnly) {
|
||||||
// App says it's installed but isn't really (and isn't track only) - set to not installed
|
// App says it's installed but isn't really (and isn't track only) - set to not installed
|
||||||
@ -741,7 +752,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
// SECOND, RECONCILE DIFFERENCES BETWEEN THE APP'S REPORTED AND REAL INSTALLED VERSIONS, WHERE NEITHER IS NULL
|
// SECOND, RECONCILE DIFFERENCES BETWEEN THE APP'S REPORTED AND REAL INSTALLED VERSIONS, WHERE NEITHER IS NULL
|
||||||
if (installedInfo?.versionName != null &&
|
if (installedInfo?.versionName != null &&
|
||||||
installedInfo!.versionName != app.installedVersion &&
|
installedInfo!.versionName != app.installedVersion &&
|
||||||
!noVersionDetection) {
|
versionDetectionIsStandard) {
|
||||||
// App's reported version and real version don't match (and it uses standard version detection)
|
// App's reported version and real version don't match (and it uses standard version detection)
|
||||||
// If they share a standard format (and are still different under it), update the reported version accordingly
|
// If they share a standard format (and are still different under it), update the reported version accordingly
|
||||||
var correctedInstalledVersion = reconcileVersionDifferences(
|
var correctedInstalledVersion = reconcileVersionDifferences(
|
||||||
@ -749,12 +760,15 @@ class AppsProvider with ChangeNotifier {
|
|||||||
if (correctedInstalledVersion?.key == false) {
|
if (correctedInstalledVersion?.key == false) {
|
||||||
app.installedVersion = correctedInstalledVersion!.value;
|
app.installedVersion = correctedInstalledVersion!.value;
|
||||||
modded = true;
|
modded = true;
|
||||||
|
} else if (naiveStandardVersionDetection) {
|
||||||
|
app.installedVersion = installedInfo.versionName;
|
||||||
|
modded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// THIRD, RECONCILE THE APP'S REPORTED INSTALLED AND LATEST VERSIONS
|
// THIRD, RECONCILE THE APP'S REPORTED INSTALLED AND LATEST VERSIONS
|
||||||
if (app.installedVersion != null &&
|
if (app.installedVersion != null &&
|
||||||
app.installedVersion != app.latestVersion &&
|
app.installedVersion != app.latestVersion &&
|
||||||
!noVersionDetection) {
|
versionDetectionIsStandard) {
|
||||||
// App's reported installed and latest versions don't match (and it uses standard version detection)
|
// App's reported installed and latest versions don't match (and it uses standard version detection)
|
||||||
// If they share a standard format, make sure the App's reported installed version uses that format
|
// If they share a standard format, make sure the App's reported installed version uses that format
|
||||||
var correctedInstalledVersion =
|
var correctedInstalledVersion =
|
||||||
@ -766,8 +780,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
// FOURTH, DISABLE VERSION DETECTION IF ENABLED AND THE REPORTED/REAL INSTALLED VERSIONS ARE NOT STANDARDIZED
|
// FOURTH, DISABLE VERSION DETECTION IF ENABLED AND THE REPORTED/REAL INSTALLED VERSIONS ARE NOT STANDARDIZED
|
||||||
if (installedInfo != null &&
|
if (installedInfo != null &&
|
||||||
app.additionalSettings['versionDetection'] ==
|
versionDetectionIsStandard &&
|
||||||
'standardVersionDetection' &&
|
|
||||||
!isVersionDetectionPossible(
|
!isVersionDetectionPossible(
|
||||||
AppInMemory(app, null, installedInfo, null))) {
|
AppInMemory(app, null, installedInfo, null))) {
|
||||||
app.additionalSettings['versionDetection'] = 'noVersionDetection';
|
app.additionalSettings['versionDetection'] = 'noVersionDetection';
|
||||||
@ -1055,7 +1068,8 @@ class AppsProvider with ChangeNotifier {
|
|||||||
|
|
||||||
Future<List<App>> checkUpdates(
|
Future<List<App>> checkUpdates(
|
||||||
{DateTime? ignoreAppsCheckedAfter,
|
{DateTime? ignoreAppsCheckedAfter,
|
||||||
bool throwErrorsForRetry = false}) async {
|
bool throwErrorsForRetry = false,
|
||||||
|
List<String>? specificIds}) async {
|
||||||
List<App> updates = [];
|
List<App> updates = [];
|
||||||
MultiAppMultiError errors = MultiAppMultiError();
|
MultiAppMultiError errors = MultiAppMultiError();
|
||||||
if (!gettingUpdates) {
|
if (!gettingUpdates) {
|
||||||
@ -1063,27 +1077,33 @@ class AppsProvider with ChangeNotifier {
|
|||||||
try {
|
try {
|
||||||
List<String> appIds = getAppsSortedByUpdateCheckTime(
|
List<String> appIds = getAppsSortedByUpdateCheckTime(
|
||||||
ignoreAppsCheckedAfter: ignoreAppsCheckedAfter);
|
ignoreAppsCheckedAfter: ignoreAppsCheckedAfter);
|
||||||
for (int i = 0; i < appIds.length; i++) {
|
if (specificIds != null) {
|
||||||
|
appIds = appIds.where((aId) => specificIds.contains(aId)).toList();
|
||||||
|
}
|
||||||
|
await Future.wait(appIds.map((appId) async {
|
||||||
App? newApp;
|
App? newApp;
|
||||||
try {
|
try {
|
||||||
newApp = await checkUpdate(appIds[i]);
|
newApp = await checkUpdate(appId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if ((e is RateLimitError || e is SocketException) &&
|
if ((e is RateLimitError || e is SocketException) &&
|
||||||
throwErrorsForRetry) {
|
throwErrorsForRetry) {
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
errors.add(appIds[i], e.toString());
|
errors.add(appId, e, appName: apps[appId]?.name);
|
||||||
}
|
}
|
||||||
if (newApp != null) {
|
if (newApp != null) {
|
||||||
updates.add(newApp);
|
updates.add(newApp);
|
||||||
}
|
}
|
||||||
}
|
}), eagerError: true);
|
||||||
} finally {
|
} finally {
|
||||||
gettingUpdates = false;
|
gettingUpdates = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (errors.content.isNotEmpty) {
|
if (errors.idsByErrorString.isNotEmpty) {
|
||||||
throw errors;
|
var res = <String, dynamic>{};
|
||||||
|
res['errors'] = errors;
|
||||||
|
res['updates'] = updates;
|
||||||
|
throw res;
|
||||||
}
|
}
|
||||||
return updates;
|
return updates;
|
||||||
}
|
}
|
||||||
@ -1300,18 +1320,16 @@ class _APKOriginWarningDialogState extends State<APKOriginWarningDialog> {
|
|||||||
|
|
||||||
/// Background updater function
|
/// Background updater function
|
||||||
///
|
///
|
||||||
/// @param List<String>? toCheck: The appIds to check for updates (default to all apps sorted by last update check time)
|
/// @param List<MapEntry<String, int>>? toCheck: The appIds to check for updates (with the number of previous attempts made per appid) (defaults to all apps)
|
||||||
///
|
///
|
||||||
/// @param List<String>? toInstall: The appIds to attempt to update (defaults to an empty array)
|
/// @param List<String>? toInstall: The appIds to attempt to update (defaults to an empty array)
|
||||||
///
|
///
|
||||||
/// @param int? attemptCount: The number of times the function has failed up to this point (defaults to 0)
|
|
||||||
///
|
|
||||||
/// When toCheck is empty, the function is in "install mode" (else it is in "update mode").
|
/// When toCheck is empty, the function is in "install mode" (else it is in "update mode").
|
||||||
/// In update mode, all apps in toCheck are checked for updates.
|
/// In update mode, all apps in toCheck are checked for updates.
|
||||||
/// If an update is available, the appId is either added to toInstall (if a background update is possible) or the user is notified.
|
/// If an update is available, the appId is either added to toInstall (if a background update is possible) or the user is notified.
|
||||||
/// If there is an error, the function tries to continue after a few minutes (duration depends on the error), up to a maximum of 5 tries.
|
/// If there are errors, the task is run again for the remaining apps after a few minutes (duration depends on the errors), up to a maximum of 5 tries for any app.
|
||||||
///
|
///
|
||||||
/// Once all update checks are complete, the function is called again in install mode.
|
/// Once all update checks are complete, the task is run again in install mode.
|
||||||
/// In this mode, all apps in toInstall are downloaded and installed in the background (install result is unknown).
|
/// In this mode, all apps in toInstall are downloaded and installed in the background (install result is unknown).
|
||||||
/// If there is an error, the function tries to continue after a few minutes (duration depends on the error), up to a maximum of 5 tries.
|
/// If there is an error, the function tries to continue after a few minutes (duration depends on the error), up to a maximum of 5 tries.
|
||||||
///
|
///
|
||||||
@ -1358,87 +1376,109 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
|||||||
'BG ${installMode ? 'install' : 'update'} task $taskId: Started (${installMode ? toInstall.length : toCheck.length}).');
|
'BG ${installMode ? 'install' : 'update'} task $taskId: Started (${installMode ? toInstall.length : toCheck.length}).');
|
||||||
|
|
||||||
if (!installMode) {
|
if (!installMode) {
|
||||||
// If in update mode...
|
// If in update mode, we check for updates.
|
||||||
var didCompleteChecking = false;
|
// We divide the results into 4 groups:
|
||||||
CheckingUpdatesNotification? notif;
|
// - 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 = [];
|
||||||
|
List<App> toNotify = [];
|
||||||
|
List<MapEntry<String, int>> toRetry = [];
|
||||||
|
var retryAfterXSeconds = 0;
|
||||||
|
List<String> toThrow = [];
|
||||||
var networkRestricted = false;
|
var networkRestricted = false;
|
||||||
if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) {
|
if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) {
|
||||||
var netResult = await (Connectivity().checkConnectivity());
|
var netResult = await (Connectivity().checkConnectivity());
|
||||||
networkRestricted = (netResult != ConnectivityResult.wifi) &&
|
networkRestricted = (netResult != ConnectivityResult.wifi) &&
|
||||||
(netResult != ConnectivityResult.ethernet);
|
(netResult != ConnectivityResult.ethernet);
|
||||||
}
|
}
|
||||||
// Loop through all updates and check each
|
MultiAppMultiError? errors;
|
||||||
List<App> toNotify = [];
|
CheckingUpdatesNotification notif =
|
||||||
|
CheckingUpdatesNotification(plural('apps', toCheck.length));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (int i = 0; i < toCheck.length; i++) {
|
// Check for updates
|
||||||
var appId = toCheck[i].key;
|
notificationsProvider.notify(notif, cancelExisting: true);
|
||||||
var attemptCount = toCheck[i].value + 1;
|
updates = await appsProvider.checkUpdates(
|
||||||
AppInMemory? app = appsProvider.apps[appId];
|
specificIds: toCheck.map((e) => e.key).toList());
|
||||||
if (app?.app.installedVersion != null) {
|
} catch (e) {
|
||||||
try {
|
// If there were errors, group them into toRetry and toThrow
|
||||||
notificationsProvider.notify(
|
if (e is Map) {
|
||||||
notif = CheckingUpdatesNotification(app?.name ?? appId),
|
updates = e['updates'];
|
||||||
cancelExisting: true);
|
errors = e['errors'];
|
||||||
App? newApp = await appsProvider.checkUpdate(appId);
|
errors!.rawErrors.forEach((key, err) {
|
||||||
if (newApp != null) {
|
logs.add(
|
||||||
if (networkRestricted ||
|
'BG update task $taskId: Got error on checking for $key \'${err.toString()}\'.');
|
||||||
!(await appsProvider.canInstallSilently(app!.app))) {
|
var toCheckApp = toCheck.where((element) => element.key == key).first;
|
||||||
toNotify.add(newApp);
|
if (toCheckApp.value < maxAttempts) {
|
||||||
} else {
|
toRetry.add(MapEntry(toCheckApp.key, toCheckApp.value + 1));
|
||||||
toInstall.add(MapEntry(appId, 0));
|
var minRetryIntervalForThisApp = err is RateLimitError
|
||||||
}
|
? (err.remainingMinutes * 60)
|
||||||
}
|
: e is ClientException
|
||||||
if (i == (toCheck.length - 1)) {
|
? (15 * 60)
|
||||||
didCompleteChecking = true;
|
: pow(toCheckApp.value + 1, 2).toInt();
|
||||||
}
|
if (minRetryIntervalForThisApp > retryAfterXSeconds) {
|
||||||
} catch (e) {
|
retryAfterXSeconds = minRetryIntervalForThisApp;
|
||||||
// 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 {
|
} finally {
|
||||||
if (toNotify.isNotEmpty) {
|
notificationsProvider.cancel(notif.id);
|
||||||
notificationsProvider.notify(UpdateNotification(toNotify));
|
}
|
||||||
|
|
||||||
|
// 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 you're done checking and found some silently installable updates, schedule another task which will run in install mode
|
|
||||||
if (didCompleteChecking && toInstall.isNotEmpty) {
|
// 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
|
||||||
logs.add(
|
logs.add(
|
||||||
'BG update task $taskId: Done. Scheduling install task to run immediately.');
|
'BG update task $taskId: Done. Scheduling install task to run immediately.');
|
||||||
AndroidAlarmManager.oneShot(
|
AndroidAlarmManager.oneShot(
|
||||||
@ -1449,11 +1489,14 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
|||||||
.map((entry) => {'key': entry.key, 'value': entry.value})
|
.map((entry) => {'key': entry.key, 'value': entry.value})
|
||||||
.toList()
|
.toList()
|
||||||
});
|
});
|
||||||
} else if (didCompleteChecking) {
|
} else {
|
||||||
logs.add('BG install task $taskId: Done.');
|
logs.add('BG install task $taskId: Done.');
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// If in install mode...
|
|
||||||
|
if (installMode) {
|
||||||
|
// If in install mode, we install silent updates.
|
||||||
|
|
||||||
var didCompleteInstalling = false;
|
var didCompleteInstalling = false;
|
||||||
var tempObtArr = toInstall.where((element) => element.key == obtainiumId);
|
var tempObtArr = toInstall.where((element) => element.key == obtainiumId);
|
||||||
if (tempObtArr.isNotEmpty) {
|
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/telegramapp.dart';
|
||||||
import 'package:obtainium/app_sources/uptodown.dart';
|
import 'package:obtainium/app_sources/uptodown.dart';
|
||||||
import 'package:obtainium/app_sources/vlc.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/components/generated_form.dart';
|
||||||
import 'package:obtainium/custom_errors.dart';
|
import 'package:obtainium/custom_errors.dart';
|
||||||
import 'package:obtainium/mass_app_sources/githubstars.dart';
|
import 'package:obtainium/mass_app_sources/githubstars.dart';
|
||||||
@ -328,6 +329,7 @@ abstract class AppSource {
|
|||||||
bool changeLogIfAnyIsMarkDown = true;
|
bool changeLogIfAnyIsMarkDown = true;
|
||||||
bool appIdInferIsOptional = false;
|
bool appIdInferIsOptional = false;
|
||||||
bool allowSubDomains = false;
|
bool allowSubDomains = false;
|
||||||
|
bool naiveStandardVersionDetection = false;
|
||||||
|
|
||||||
AppSource() {
|
AppSource() {
|
||||||
name = runtimeType.toString();
|
name = runtimeType.toString();
|
||||||
@ -556,7 +558,7 @@ class SourceProvider {
|
|||||||
Mullvad(),
|
Mullvad(),
|
||||||
Signal(),
|
Signal(),
|
||||||
VLC(),
|
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(),
|
||||||
NeutronCode(),
|
NeutronCode(),
|
||||||
|
40
pubspec.lock
40
pubspec.lock
@ -46,10 +46,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "20071638cbe4e5964a427cfa0e86dce55d060bc7d82d56f3554095d7239a8765"
|
sha256: "06a96f1249f38a00435b3b0c9a3246d934d7dbc8183fc7c9e56989860edb99d4"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.2"
|
version: "3.4.4"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -291,10 +291,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_local_notifications
|
name: flutter_local_notifications
|
||||||
sha256: "3002092e5b8ce2f86c3361422e52e6db6776c23ee21e0b2f71b892bf4259ef04"
|
sha256: "6d11ea777496061e583623aaf31923f93a9409ef8fcaeeefdd6cd78bf4fe5bb3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "15.1.1"
|
version: "16.1.0"
|
||||||
flutter_local_notifications_linux:
|
flutter_local_notifications_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -320,10 +320,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_markdown
|
name: flutter_markdown
|
||||||
sha256: a10979814c5f4ddbe2b6143fba25d927599e21e3ba65b3862995960606fae78f
|
sha256: "8afc9a6aa6d8e8063523192ba837149dbf3d377a37c0b0fc579149a1fbd4a619"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.17+3"
|
version: "0.6.18"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -386,10 +386,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: "6e703d5e2f8c63fb31a77753915c1ec8baebde8088844e0d29f71b8f0b108888"
|
sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.0"
|
version: "4.1.3"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -538,18 +538,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: permission_handler
|
name: permission_handler
|
||||||
sha256: ad65ba9af42a3d067203641de3fd9f547ded1410bad3b84400c2b4899faede70
|
sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.0.0"
|
version: "11.0.1"
|
||||||
permission_handler_android:
|
permission_handler_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_android
|
name: permission_handler_android
|
||||||
sha256: f2543a236584a5e8be79076f858022f100ce690e31530e6fa4c32ac94f276d3a
|
sha256: ace7d15a3d1a4a0b91c041d01e5405df221edb9de9116525efc773c74e6fc790
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.0.3"
|
version: "11.0.5"
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -879,18 +879,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: webview_flutter
|
name: webview_flutter
|
||||||
sha256: "82f6787d5df55907aa01e49bd9644f4ed1cc82af7a8257dd9947815959d2e755"
|
sha256: c1ab9b81090705c6069197d9fdc1625e587b52b8d70cdde2339d177ad0dbb98e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.4"
|
version: "4.4.1"
|
||||||
webview_flutter_android:
|
webview_flutter_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_android
|
name: webview_flutter_android
|
||||||
sha256: ddc167c6676f57c8b367d19fcbee267d6dc6adf81bd6c3cb87981d30746e0a6d
|
sha256: b0cd33dd7d3dd8e5f664e11a19e17ba12c352647269921a3b568406b001f1dff
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.10.1"
|
version: "3.12.0"
|
||||||
webview_flutter_platform_interface:
|
webview_flutter_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -903,18 +903,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_wkwebview
|
name: webview_flutter_wkwebview
|
||||||
sha256: "485af05f2c5f83c7f78c20e236b170ad02df7153b299ae9917345be43871d29f"
|
sha256: "30b9af6bdd457b44c08748b9190d23208b5165357cc2eb57914fee1366c42974"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.8.0"
|
version: "3.9.1"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: c97defd418eef4ec88c0d1652cdce84b9f7b63dd7198e266d06ac1710d527067
|
sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.8"
|
version: "5.0.9"
|
||||||
win32_registry:
|
win32_registry:
|
||||||
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.19+211 # When changing this, update the tag in main() accordingly
|
version: 0.14.24+216 # When changing this, update the tag in main() accordingly
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.0.0 <4.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
@ -38,7 +38,7 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.5
|
cupertino_icons: ^1.0.5
|
||||||
path_provider: ^2.0.11
|
path_provider: ^2.0.11
|
||||||
flutter_fgbg: ^0.3.0 # Try removing reliance on this
|
flutter_fgbg: ^0.3.0 # Try removing reliance on this
|
||||||
flutter_local_notifications: ^15.1.0+1
|
flutter_local_notifications: ^16.1.0
|
||||||
provider: ^6.0.3
|
provider: ^6.0.3
|
||||||
http: ^1.0.0
|
http: ^1.0.0
|
||||||
webview_flutter: ^4.0.0
|
webview_flutter: ^4.0.0
|
||||||
|
Reference in New Issue
Block a user