Compare commits

...

148 Commits

Author SHA1 Message Date
a0ecb4e2bb Merge pull request #1003 from ImranR98/dev
Fix Source selection (HTML) which broke in the last release
2023-10-14 05:24:49 -04:00
a726b09f26 Fix Source selection (HTML) which broke in the last release 2023-10-14 05:24:08 -04:00
52b1d1fe33 Merge pull request #1001 from ImranR98/dev
Fixed syntax errors in de.json
2023-10-14 01:36:49 -04:00
8b4709c132 Fixed syntax errors in de.json 2023-10-14 01:36:26 -04:00
b4217a791e Merge pull request #994 from DwainZwerg/patch-3
Update de.json
2023-10-14 01:33:33 -04:00
9b3a982d54 Merge pull request #997 from Daviteusz/weblate-obtainium-translate
locale(pl): Update Polish translation
2023-10-14 01:33:11 -04:00
0a47d3eab5 Merge pull request #1000 from ImranR98/dev
Switch back to parallelized BG task (#963), Make Third Party F-Droid Repos Searchable (#995)
2023-10-14 01:31:50 -04:00
7d7803247d Update packages, increment version 2023-10-14 01:30:37 -04:00
aa8d45e636 Make Third Party F-Droid Repos Searchable (#995) 2023-10-14 01:29:02 -04:00
7f3e87767c Switch back to parallelized BG task (#963) + better logging 2023-10-13 23:39:24 -04:00
1eefeae060 locale(pl): Update Polish translations 2023-10-13 13:03:33 +02:00
03447c33be Update de.json 2023-10-13 07:30:23 +00:00
f6c6cd5cb8 Merge pull request #993 from ImranR98/dev
Separate notif channel title translations (#979), Add 'skip update notification' per-app toggle (#980), Better error for some incompatible APK cases (#981), Add whole-page version extraction for HTML (#989), Better relative link resolution (#989), Clearer wording (#991), Fixed issues in some translation files (#992)
2023-10-12 19:54:12 -04:00
67b56cee50 Upgrade packages, increment version 2023-10-12 19:51:55 -04:00
2d7465cca6 Separate notif channel title translations (#979) 2023-10-12 19:51:04 -04:00
d06f20690d Add 'skip update notification' per-app toggle (#980) 2023-10-12 19:33:38 -04:00
e613a494ff Better error for some incompatible APK cases (#981) 2023-10-12 19:24:23 -04:00
503a01675e Clearer wording (#991) 2023-10-12 19:10:02 -04:00
723f7e63e9 Add whole-page version extraction for HTML (#989) 2023-10-12 19:06:59 -04:00
7ff6916e5f Better relative link resolution (#989) 2023-10-12 18:57:47 -04:00
910ed26525 Fixed issues in some translation files 2023-10-12 18:31:31 -04:00
c0fffa6293 Merge pull request #987 from ImranR98/dev
Fix issues with last conflit resolution commit
2023-10-11 21:07:18 -04:00
d800d3ba41 Fix issues with last conflit resolution commit 2023-10-11 21:03:23 -04:00
d267343c64 Merge pull request #986 from ImranR98/dev
Switch back to serial BG installs (#984), Fix UpToDown again (#982)
2023-10-11 21:01:09 -04:00
0f72e37272 Merge branch 'main' into dev 2023-10-11 21:00:37 -04:00
89e44d581f Update packages, increment version 2023-10-11 20:56:26 -04:00
c908a710de Fix UpToDown again (#982) 2023-10-11 20:55:09 -04:00
f0809cde72 Merge pull request #977 from iDazai/main
updated translations
2023-10-11 20:44:12 -04:00
e6da8c1c18 Merge pull request #978 from Daviteusz/weblate-obtainium-translate
locale(pl): Update Polish translation
2023-10-11 20:44:06 -04:00
81ea67afa8 Remove unnecessary log 2023-10-11 20:43:27 -04:00
488049821e Switch back to serial BG installs (#984) 2023-10-11 20:42:33 -04:00
fc7408a8f8 locale(pl): Update Polish translations 2023-10-09 20:14:22 +02:00
6955191fdd Update pl.json
fixed line breaks
2023-10-09 15:22:32 +02:00
ba59693d90 Update pl.json
Changed order of the lines so it's the same order for every language and better to compare
2023-10-09 15:17:52 +02:00
ce6e51128d Update it.json
added three missing lines, no translation
2023-10-09 15:04:45 +02:00
a7a26037ae Update hu.json
added four missing lines, no translation
2023-10-09 15:02:51 +02:00
00854abd1a Update fr.json
added three missing lines, no translation
2023-10-09 14:59:14 +02:00
c26649d5f2 Update fa.json
added three missing lines, no translation
2023-10-09 14:58:42 +02:00
63b76a249f Update es.json
added three missing lines, no translation
2023-10-09 14:57:52 +02:00
95f19208ad Update bs.json
added three missing lines, no translation
2023-10-09 14:54:24 +02:00
f4a902ee47 Update de.json
added three missing lines and translated them, reverted translation of variables so log function can work again.
2023-10-09 14:52:50 +02:00
036823cbb6 Merge remote-tracking branch 'origin/main' into dev 2023-10-08 17:33:12 -04:00
547c2c8c0d Merge pull request #975 from ImranR98/dev
Less notif spam, no check in aeroplane mode (#974)
2023-10-08 17:33:05 -04:00
7453d19d27 Increment version, update packages 2023-10-08 17:32:41 -04:00
2a0b68d637 Less notif spam, no check in aeroplane mode (#974) 2023-10-08 17:30:08 -04:00
02f374fdf0 Merge pull request #969 from ImranR98/dev
Enabled WhatsApp Source (#357)
2023-10-07 13:31:45 -04:00
3536dcd775 Increment version 2023-10-07 13:31:02 -04:00
6fa887e536 Merge pull request #967 from JohnBetaro/main
Better version detection for F-Droid
2023-10-07 13:16:39 -04:00
71755763b9 Enabled WhatsApp Source (#357) 2023-10-07 13:15:22 -04:00
5aabcccfd4 Update fdroid.dart 2023-10-07 09:53:34 +01:00
1e009e2211 Update release.yml 2023-10-07 02:03:18 -04:00
811bc6434b Update release.yml 2023-10-07 02:00:13 -04:00
a5b2d06742 Update release.yml 2023-10-07 01:33:34 -04:00
2c00674835 Update release.yml 2023-10-07 01:20:03 -04:00
9bbff1f436 Update release.yml 2023-10-07 01:08:43 -04:00
c75416f9be Update release.yml 2023-10-07 01:02:53 -04:00
2cb750a7b0 Update release.yml 2023-10-07 00:56:27 -04:00
abfec51964 Update release.yml 2023-10-07 00:40:34 -04:00
e314717a3e Update release.yml 2023-10-07 00:33:50 -04:00
290a78acb5 Update release.yml 2023-10-07 00:30:09 -04:00
b40a4dd243 Update release.yml 2023-10-07 00:09:18 -04:00
b38f6d0581 Update release.yml 2023-10-06 23:58:33 -04:00
5c562a6bdb Update release.yml 2023-10-06 23:45:00 -04:00
238d43f916 Update release.yml 2023-10-06 23:18:25 -04:00
818dc23089 Update release.yml 2023-10-06 22:55:59 -04:00
ddb50940ce Update release.yml 2023-10-06 22:55:50 -04:00
96fb97f4ef Update release.yml 2023-10-06 22:53:13 -04:00
6c0459c2ae Update release.yml 2023-10-06 22:51:30 -04:00
30a9e1ee4b Update release.yml 2023-10-06 22:48:21 -04:00
15a1f1e6e0 Update release.yml 2023-10-06 22:46:11 -04:00
827e2b161c Update release.yml 2023-10-06 22:45:20 -04:00
e95230859e Update release.yml 2023-10-06 22:43:41 -04:00
95cb8ca493 Update release.yml 2023-10-06 22:41:47 -04:00
5735293efb Update release.yml 2023-10-06 22:40:27 -04:00
035df1f338 Update release.yml 2023-10-06 22:38:01 -04:00
36a8b04c4f Update release.yml 2023-10-06 22:35:40 -04:00
3d5411ef28 Update release.yml 2023-10-06 22:32:23 -04:00
df7b74e727 Update release.yml 2023-10-06 22:28:02 -04:00
0138599120 Update release.yml 2023-10-06 22:23:45 -04:00
66446cbffc Update release.yml 2023-10-06 22:16:41 -04:00
ef40f3900f Update release.yml 2023-10-06 22:11:25 -04:00
ce259764a8 Update release.yml 2023-10-06 22:09:33 -04:00
65187cb17b Update release.yml 2023-10-06 22:04:55 -04:00
8f25245ad1 Update release.yml 2023-10-06 21:47:25 -04:00
58b8baa019 Merge pull request #966 from ImranR98/dev
Run GH action manually only
2023-10-06 21:21:44 -04:00
ba1db88c8d Run GH action manually only 2023-10-06 21:21:26 -04:00
c8f2f42a35 Merge pull request #965 from ImranR98/dev
Parallelize Update Checking (#963), Bugfixes (#954, #962)
2023-10-06 21:20:19 -04:00
4e5a9b2af5 Updated GitHub actions 2023-10-06 21:19:46 -04:00
d1ab961c14 Merge remote-tracking branch 'origin/main' into dev 2023-10-06 20:45:02 -04:00
33caf4644e Ran dart fix 2023-10-06 20:43:06 -04:00
f34ffe18a8 Update packages, increment version 2023-10-06 20:42:09 -04:00
4b1c5e111d Merge pull request #958 from JohnBetaro/main
Workflow to build and release
2023-10-06 20:40:48 -04:00
dbd7c296c6 Merge pull request #951 from DwainZwerg/patch-2
Update de.json
2023-10-06 20:38:08 -04:00
df93cbde8f Fix UpToDown (#962) 2023-10-06 20:37:14 -04:00
d8cbc121a7 Merge pull request #964 from ImranR98/963
Parallelize Update Checking (#963)
2023-10-06 20:07:18 -04:00
8163cd5c8f Improvements, bugfixes 2023-10-06 19:58:46 -04:00
e9e9adb174 Tweaks 2023-10-06 19:34:08 -04:00
21fdfc1eef Attempting to parallelize update checks 2023-10-06 19:23:18 -04:00
4304251e1e Remove checking for APKs
This is because I couldn't check for prereleases because I don't know how, maybe you can.
2023-10-05 11:31:42 +01:00
ae0cd74b0e Update android.yml 2023-10-05 11:29:03 +01:00
6935bff244 Update android.yml 2023-10-05 11:22:29 +01:00
102b9da617 Update android.yml 2023-10-05 11:19:46 +01:00
8bd0d185ae Create android.yml 2023-10-05 11:16:42 +01:00
7bfc5ae0a8 Bugfix (#954) 2023-10-03 21:34:24 -04:00
c72a41db9d Update de.json
and small improvements
2023-10-01 12:05:49 +00:00
5fbb1c2e32 Merge pull request #947 from LilligantMatsuri/main
Update Chinese translation
2023-09-30 10:16:52 -04:00
850dd53c69 Merge pull request #949 from ImranR98/dev
"Naive" version detection for some Sources (#946)
2023-09-30 10:16:36 -04:00
e3baf91037 Increment version, upgrade packages 2023-09-30 10:16:10 -04:00
4c811c9c04 "Naive" version detection for some Sources (#946) 2023-09-30 10:12:49 -04:00
4bc9f5826e Update zh.json
- Translate new strings
- Slight improvements

Signed-off-by: Matsuri <matsuri@vmoe.info>
2023-09-30 20:30:00 +08:00
9f19e4dc83 Merge pull request #945 from ImranR98/dev
Add export dir pick button + update packages
2023-09-29 12:13:38 -04:00
c9cb865c9c Add export dir pick button + update packages 2023-09-29 12:13:11 -04:00
b8a7dd984d Merge pull request #934 from LucasTavaresA/main
Update pt.json
2023-09-29 12:12:55 -04:00
3ede46c445 Merge pull request #937 from TangyWrecker/main
Update ru.json
2023-09-29 12:12:39 -04:00
87c4af94d3 Merge pull request #944 from Daviteusz/weblate-obtainium-translate
locale(pl): Update Polish translation
2023-09-29 12:12:32 -04:00
7e85ccaf28 locale(pl): Update Polish translations 2023-09-29 02:39:18 +02:00
db3a262410 Update ru.json
oops
2023-09-26 01:13:01 +03:00
70162da491 Update ru.json 2023-09-26 01:09:19 +03:00
f3632a4033 Update pt.json 2023-09-24 15:22:56 -03:00
2f4e176116 Merge pull request #933 from ImranR98/dev
Downgrade permission handler to fix #932 + APKPure improvement
2023-09-23 17:19:52 -04:00
3ea8dc598c Downgrade permission handler to fix #932 + APKPure improvement 2023-09-23 17:18:12 -04:00
3f63dc0180 Merge pull request #930 from gidano/main
Update hu.json
2023-09-23 14:37:19 -04:00
1f6701b183 Update hu.json 2023-09-23 20:36:00 +02:00
3a170fbf90 Merge pull request #931 from ImranR98/dev
Fix major GitHub bug from previous release (#929)
2023-09-23 14:32:46 -04:00
b57e523a07 Increment version 2023-09-23 14:32:13 -04:00
8c4509f255 Fix major GitHub bug (#929) 2023-09-23 14:31:52 -04:00
3878c287be Update hu.json 2023-09-23 20:23:25 +02:00
7789e3f3a7 Removed excessive logs 2023-09-23 14:02:55 -04:00
94ac744f41 Merge pull request #928 from ImranR98/dev
Fix version number
2023-09-23 13:43:25 -04:00
a83211f260 Fix version number 2023-09-23 13:43:04 -04:00
4f733affc5 Merge pull request #927 from ImranR98/dev
- Change a default F-Droid Source option
- Add GitHub "don't sort" toggle (#925)
- Better IzzyOnDroid support (#926)
- Allow 'm.' subdomain for APKPure
- Added HTML reverse sorting (#915)
2023-09-23 13:41:08 -04:00
ffb2a982bc Merge branch 'main' into dev 2023-09-23 13:40:32 -04:00
9658a65661 Upgrade packages, increment version 2023-09-23 13:36:46 -04:00
68d4083708 Added HTML reverse sorting (#915)
+ translatable strings (#916)
2023-09-23 13:31:27 -04:00
af5377d2eb Allow 'm.' subdomain for APKPure 2023-09-23 13:21:48 -04:00
6ac8ad543c Better IzzyOnDroid support (#926) 2023-09-23 13:16:51 -04:00
c10a63e76e Add GitHub "don't sort" toggle (#925) 2023-09-23 12:48:21 -04:00
b423a2af52 Change a default F-Droid Source option 2023-09-22 00:22:54 -04:00
9b70289345 Merge pull request #914 from ygxbnet/main
Update zh.json
2023-09-18 20:11:08 -04:00
2a94c1f33a Update main.dart 2023-09-19 00:51:58 +08:00
ea205c1acb Update zh.json 2023-09-19 00:32:20 +08:00
6e1767730b Update zh.json 2023-09-18 00:45:45 +08:00
3fc1ab37d7 Merge pull request #911 from ImranR98/dev
Enable Brazil translation (#830)
2023-09-16 17:41:58 -04:00
c4a3f7333d Enable Brazil translation (#830) 2023-09-16 16:45:06 -04:00
fb820e01b1 Merge pull request #910 from ImranR98/dev
Remove nonsense hint
2023-09-16 16:35:37 -04:00
504aca5287 Merge pull request #908 from oSoWoSo/cz
Add Czech translation
2023-09-16 16:35:25 -04:00
ae7d5546ae Remove nonsense hint 2023-09-16 16:34:15 -04:00
8bcb10b281 Add Czech translation 2023-09-16 22:32:42 +02:00
36 changed files with 1999 additions and 1112 deletions

70
.github/workflows/release.yml vendored Normal file
View 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

View File

@ -1,310 +1,329 @@
{ {
"invalidURLForSource": "Nije važeći URL aplikacije {}", "invalidURLForSource": "Nije važeći URL aplikacije {}",
"noReleaseFound": "Nije moguće pronaći odgovarajuće izdanje", "noReleaseFound": "Nije moguće pronaći odgovarajuće izdanje",
"noVersionFound": "Nije moguće odrediti verziju izdanja", "noVersionFound": "Nije moguće odrediti verziju izdanja",
"urlMatchesNoSource": "URL se ne podudara s poznatim izvorom", "urlMatchesNoSource": "URL se ne podudara s poznatim izvorom",
"cantInstallOlderVersion": "Nije moguće instalirati stariju verziju aplikacije", "cantInstallOlderVersion": "Nije moguće instalirati stariju verziju aplikacije",
"appIdMismatch": "ID preuzetog paketa se ne podudara s postojećim ID-om aplikacije", "appIdMismatch": "ID preuzetog paketa se ne podudara s postojećim ID-om aplikacije",
"functionNotImplemented": "Ova klasa nije implementirala ovu funkciju", "functionNotImplemented": "Ova klasa nije implementirala ovu funkciju",
"placeholder": "Rezervirano mjesto", "placeholder": "Rezervirano mjesto",
"someErrors": "Došlo je do nekih grešaka", "someErrors": "Došlo je do nekih grešaka",
"unexpectedError": "Neočekivana greška", "unexpectedError": "Neočekivana greška",
"ok": "Dobro", "ok": "Dobro",
"and": "i", "and": "i",
"githubPATLabel": "GitHub token za lični pristup (eng. PAT, povećava ograničenje stope)", "githubPATLabel": "GitHub token za lični pristup (eng. PAT, povećava ograničenje stope)",
"includePrereleases": "Uključi preliminarna izdanja", "includePrereleases": "Uključi preliminarna izdanja",
"fallbackToOlderReleases": "Povratak na starija izdanja", "fallbackToOlderReleases": "Povratak na starija izdanja",
"filterReleaseTitlesByRegEx": "Filtrirajte naslove izdanja prema regularnom izrazu", "filterReleaseTitlesByRegEx": "Filtrirajte naslove izdanja prema regularnom izrazu",
"invalidRegEx": "Nevažeći regularni izraz", "invalidRegEx": "Nevažeći regularni izraz",
"noDescription": "Bez opisa", "noDescription": "Bez opisa",
"cancel": "Otkaži", "cancel": "Otkaži",
"continue": "Nastavite", "continue": "Nastavite",
"requiredInBrackets": "(obavezno)", "requiredInBrackets": "(obavezno)",
"dropdownNoOptsError": "GREŠKA: PADAJUĆI MENI MORA IMATI NAJMANJE JEDNU OPCIJU", "dropdownNoOptsError": "GREŠKA: PADAJUĆI MENI MORA IMATI NAJMANJE JEDNU OPCIJU",
"colour": "Boja", "colour": "Boja",
"githubStarredRepos": "GitHub repo-i sa zvjezdicom", "githubStarredRepos": "GitHub repo-i sa zvjezdicom",
"uname": "Korisničko ime", "uname": "Korisničko ime",
"wrongArgNum": "Naveden je pogrešan broj argumenata", "wrongArgNum": "Naveden je pogrešan broj argumenata",
"xIsTrackOnly": "{} je samo za praćenje", "xIsTrackOnly": "{} je samo za praćenje",
"source": "Izvor", "source": "Izvor",
"app": "Aplikacija. ", "app": "Aplikacija. ",
"appsFromSourceAreTrackOnly": "Aplikacije iz ovog izvora su 'Samo za praćenje'.", "appsFromSourceAreTrackOnly": "Aplikacije iz ovog izvora su 'Samo za praćenje'.",
"youPickedTrackOnly": "Odabrali ste opciju „Samo za praćenje”.", "youPickedTrackOnly": "Odabrali ste opciju „Samo za praćenje”.",
"trackOnlyAppDescription": "Aplikacija će se pratiti radi ažuriranja, ali Obtainium neće moći da je preuzme ili instalira.", "trackOnlyAppDescription": "Aplikacija će se pratiti radi ažuriranja, ali Obtainium neće moći da je preuzme ili instalira.",
"cancelled": "Otkazano", "cancelled": "Otkazano",
"appAlreadyAdded": "Aplikacija je već dodana", "appAlreadyAdded": "Aplikacija je već dodana",
"alreadyUpToDateQuestion": "Aplikacija je već ažurirana?", "alreadyUpToDateQuestion": "Aplikacija je već ažurirana?",
"addApp": "Dodaj aplikaciju", "addApp": "Dodaj aplikaciju",
"appSourceURL": "Izvorni URL aplikacije", "appSourceURL": "Izvorni URL aplikacije",
"error": "Greška", "error": "Greška",
"add": "Dodaj", "add": "Dodaj",
"searchSomeSourcesLabel": "Pretraživanje (samo neki izvori)", "searchSomeSourcesLabel": "Pretraživanje (samo neki izvori)",
"search": "Pretraživanje", "search": "Pretraživanje",
"additionalOptsFor": "Dodatne opcije za {}", "additionalOptsFor": "Dodatne opcije za {}",
"supportedSources": "Podržani izvori", "supportedSources": "Podržani izvori",
"trackOnlyInBrackets": "(Samo za praćenje)", "trackOnlyInBrackets": "(Samo za praćenje)",
"searchableInBrackets": "(Može se pretraživati)", "searchableInBrackets": "(Može se pretraživati)",
"appsString": "Aplikacije", "appsString": "Aplikacije",
"noApps": "Nema aplikacija", "noApps": "Nema aplikacija",
"noAppsForFilter": "Nema aplikacija za filter", "noAppsForFilter": "Nema aplikacija za filter",
"byX": "Autor {}", "byX": "Autor {}",
"percentProgress": "Napredak: {}%", "percentProgress": "Napredak: {}%",
"pleaseWait": "Molimo sačekajte", "pleaseWait": "Molimo sačekajte",
"updateAvailable": "Ažuriranje dostupno", "updateAvailable": "Ažuriranje dostupno",
"estimateInBracketsShort": "(Procjena)", "estimateInBracketsShort": "(Procjena)",
"notInstalled": "Nije instalirano", "notInstalled": "Nije instalirano",
"estimateInBrackets": "(Procjena)", "estimateInBrackets": "(Procjena)",
"selectAll": "Označi sve", "selectAll": "Označi sve",
"deselectN": "Poništi odabir {}", "deselectN": "Poništi odabir {}",
"xWillBeRemovedButRemainInstalled": "{} će biti uklonjen iz Obtainiuma, ali će ostati instaliran na uređaju.", "xWillBeRemovedButRemainInstalled": "{} će biti uklonjen iz Obtainiuma, ali će ostati instaliran na uređaju.",
"removeSelectedAppsQuestion": "Želite li ukloniti odabrane aplikacije?", "removeSelectedAppsQuestion": "Želite li ukloniti odabrane aplikacije?",
"removeSelectedApps": "Ukloni odabrane aplikacije", "removeSelectedApps": "Ukloni odabrane aplikacije",
"updateX": "Nadogradi {}", "updateX": "Nadogradi {}",
"installX": "Instaliraj {}", "installX": "Instaliraj {}",
"markXTrackOnlyAsUpdated": "Označi {}\n(samo za praćenje)\nkao ažurirano", "markXTrackOnlyAsUpdated": "Označi {}\n(samo za praćenje)\nkao ažurirano",
"changeX": "Promjena {}", "changeX": "Promjena {}",
"installUpdateApps": "Instalirajte/ažurirajte aplikacije", "installUpdateApps": "Instalirajte/ažurirajte aplikacije",
"installUpdateSelectedApps": "Instalirajte/ažurirajte odabrane aplikacije", "installUpdateSelectedApps": "Instalirajte/ažurirajte odabrane aplikacije",
"markXSelectedAppsAsUpdated": "Označite {} odabrane aplikacije kao ažurirane?", "markXSelectedAppsAsUpdated": "Označite {} odabrane aplikacije kao ažurirane?",
"no": "Ne", "no": "Ne",
"yes": "Da", "yes": "Da",
"markSelectedAppsUpdated": "Označi odabrane aplikacije kao ažurirane", "markSelectedAppsUpdated": "Označi odabrane aplikacije kao ažurirane",
"pinToTop": "Prikvači na vrh", "pinToTop": "Prikvači na vrh",
"unpinFromTop": "Otkvači sa vrha", "unpinFromTop": "Otkvači sa vrha",
"resetInstallStatusForSelectedAppsQuestion": "Resetujte status instalacije za odabrane aplikacije?", "resetInstallStatusForSelectedAppsQuestion": "Resetujte status instalacije za odabrane aplikacije?",
"installStatusOfXWillBeResetExplanation": "Status instalacije bilo koje odabrane aplikacije će se resetovati.\n\nTo može pomoći kada je verzija aplikacije prikazana u Obtainiumu netačna zbog neuspjelih ažuriranja ili drugih problema.", "installStatusOfXWillBeResetExplanation": "Status instalacije bilo koje odabrane aplikacije će se resetovati.\n\nTo može pomoći kada je verzija aplikacije prikazana u Obtainiumu netačna zbog neuspjelih ažuriranja ili drugih problema.",
"shareSelectedAppURLs": "Podijeli odabrane URL-ove aplikacija", "shareSelectedAppURLs": "Podijeli odabrane URL-ove aplikacija",
"resetInstallStatus": "Resetujte status instalacije", "resetInstallStatus": "Resetujte status instalacije",
"more": "Više", "more": "Više",
"removeOutdatedFilter": "Uklonite zastarjeli filter aplikacija", "removeOutdatedFilter": "Uklonite zastarjeli filter aplikacija",
"showOutdatedOnly": "Prikaži samo zastarjele aplikacije", "showOutdatedOnly": "Prikaži samo zastarjele aplikacije",
"filter": "Filtriranje", "filter": "Filtriranje",
"filterActive": "Filtriranje", "filterActive": "Filtriranje",
"filterApps": "Filtriraj aplikacije", "filterApps": "Filtriraj aplikacije",
"appName": "Naziv aplikacije", "appName": "Naziv aplikacije",
"author": "Autor", "author": "Autor",
"upToDateApps": "Ažurirane aplikacije", "upToDateApps": "Ažurirane aplikacije",
"nonInstalledApps": "Neinstalirane aplikacije", "nonInstalledApps": "Neinstalirane aplikacije",
"importExport": "Uvoz/izvoz", "importExport": "Uvoz/izvoz",
"settings": "Postavke", "settings": "Postavke",
"exportedTo": "Izvezeno u {}", "exportedTo": "Izvezeno u {}",
"obtainiumExport": "Obtainium Export", "obtainiumExport": "Obtainium Export",
"invalidInput": "Neispravan unos.", "invalidInput": "Neispravan unos.",
"importedX": "Uvezeno {}", "importedX": "Uvezeno {}",
"obtainiumImport": "Obtainium uvoz", "obtainiumImport": "Obtainium uvoz",
"importFromURLList": "Uvoz iz URL liste", "importFromURLList": "Uvoz iz URL liste",
"searchQuery": "Pretraga za: ", "searchQuery": "Pretraga za: ",
"appURLList": "Lista URL adresa aplikacija", "appURLList": "Lista URL adresa aplikacija",
"line": "Linija", "line": "Linija",
"searchX": "Pretraživanje {}", "searchX": "Pretraživanje {}",
"noResults": "Nema rezultata", "noResults": "Nema rezultata",
"importX": "Uvoz {}", "importX": "Uvoz {}",
"importedAppsIdDisclaimer": "Uvezene aplikacije mogu se pogrešno prikazati kao „Nije instalirano”.\nDa biste to riješili, ponovo ih instalirajte putem aplikacije Obtainium.\nTo ne bi trebalo uticati na podatke aplikacije.\n\nUtječe samo na URL i metode uvoza treće strane.", "importedAppsIdDisclaimer": "Uvezene aplikacije mogu se pogrešno prikazati kao „Nije instalirano”.\nDa biste to riješili, ponovo ih instalirajte putem aplikacije Obtainium.\nTo ne bi trebalo uticati na podatke aplikacije.\n\nUtječe samo na URL i metode uvoza treće strane.",
"importErrors": "Uvezi greške", "importErrors": "Uvezi greške",
"importedXOfYApps": "{} od {} aplikacija uvezeno.", "importedXOfYApps": "{} od {} aplikacija uvezeno.",
"followingURLsHadErrors": "Sljedeći URL-ovi su imali greške:", "followingURLsHadErrors": "Sljedeći URL-ovi su imali greške:",
"okay": "Dobro", "okay": "Dobro",
"selectURL": "Odaberite URL", "selectURL": "Odaberite URL",
"selectURLs": "Odaberite URL-ove", "selectURLs": "Odaberite URL-ove",
"pick": "Odaberi", "pick": "Odaberi",
"theme": "Tema", "theme": "Tema",
"dark": "Tamna", "dark": "Tamna",
"light": "Svijetla", "light": "Svijetla",
"followSystem": "Pratite sistem", "followSystem": "Pratite sistem",
"obtainium": "Obtainium", "obtainium": "Obtainium",
"materialYou": "Material You", "materialYou": "Material You",
"useBlackTheme": "Koristite čisto crnu tamnu temu", "useBlackTheme": "Koristite čisto crnu tamnu temu",
"appSortBy": "Aplikacije sortirane po", "appSortBy": "Aplikacije sortirane po",
"authorName": "Autor/Ime", "authorName": "Autor/Ime",
"nameAuthor": "Ime/Autor", "nameAuthor": "Ime/Autor",
"asAdded": "Kao što je dodano", "asAdded": "Kao što je dodano",
"appSortOrder": "Redoslijed sortiranja aplikacija", "appSortOrder": "Redoslijed sortiranja aplikacija",
"ascending": "Uzlazno", "ascending": "Uzlazno",
"descending": "Silazno", "descending": "Silazno",
"bgUpdateCheckInterval": "Interval provjere ažuriranja u pozadini", "bgUpdateCheckInterval": "Interval provjere ažuriranja u pozadini",
"neverManualOnly": "Nikada - samo ručno", "neverManualOnly": "Nikada - samo ručno",
"appearance": "Izgled", "appearance": "Izgled",
"showWebInAppView": "Prikaži izvornu web stranicu u prikazu aplikacije", "showWebInAppView": "Prikaži izvornu web stranicu u prikazu aplikacije",
"pinUpdates": "Prikvačite ažuriranja na vrh prikaza aplikacija", "pinUpdates": "Prikvačite ažuriranja na vrh prikaza aplikacija",
"updates": "Nadogradnje", "updates": "Nadogradnje",
"sourceSpecific": "Specifično za izvor", "sourceSpecific": "Specifično za izvor",
"appSource": "Izvor aplikacije", "appSource": "Izvor aplikacije",
"noLogs": "Nema evidencije", "noLogs": "Nema evidencije",
"appLogs": "Evidencije aplikacija", "appLogs": "Evidencije aplikacija",
"close": "Zatvori", "close": "Zatvori",
"share": "Podijeli", "share": "Podijeli",
"appNotFound": "Aplikacija nije pronađena", "appNotFound": "Aplikacija nije pronađena",
"obtainiumExportHyphenatedLowercase": "obtainium-export", "obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "Odaberite APK", "pickAnAPK": "Odaberite APK",
"appHasMoreThanOnePackage": "{} ima više od jednog paketa:", "appHasMoreThanOnePackage": "{} ima više od jednog paketa:",
"deviceSupportsXArch": "Vaš uređaj podržava {} arhitekturu procesora.", "deviceSupportsXArch": "Vaš uređaj podržava {} arhitekturu procesora.",
"deviceSupportsFollowingArchs": "Vaš uređaj podržava sljedeće arhitekture procesora:", "deviceSupportsFollowingArchs": "Vaš uređaj podržava sljedeće arhitekture procesora:",
"warning": "Upozorenje", "warning": "Upozorenje",
"sourceIsXButPackageFromYPrompt": "Izvor aplikacije je '{}', ali paket za izdavanje dolazi iz '{}'. Želite li nastaviti?", "sourceIsXButPackageFromYPrompt": "Izvor aplikacije je '{}', ali paket za izdavanje dolazi iz '{}'. Želite li nastaviti?",
"updatesAvailable": "Dostupna ažuriranja", "updatesAvailable": "Dostupna ažuriranja",
"updatesAvailableNotifDescription": "Obavještava korisnika da su ažuriranja dostupna za jednu ili više aplikacija koje prati Obtainium", "updatesAvailableNotifDescription": "Obavještava korisnika da su ažuriranja dostupna za jednu ili više aplikacija koje prati Obtainium",
"noNewUpdates": "Nema novih ažuriranja.", "noNewUpdates": "Nema novih ažuriranja.",
"xHasAnUpdate": "{} ima ažuriranje.", "xHasAnUpdate": "{} ima ažuriranje.",
"appsUpdated": "Aplikacije su ažurirane", "appsUpdated": "Aplikacije su ažurirane",
"appsUpdatedNotifDescription": "Obavještava korisnika da su u pozadini primijenjena ažuriranja na jednu ili više aplikacija", "appsUpdatedNotifDescription": "Obavještava korisnika da su u pozadini primijenjena ažuriranja na jednu ili više aplikacija",
"xWasUpdatedToY": "{} je ažuriran na {}.", "xWasUpdatedToY": "{} je ažuriran na {}.",
"errorCheckingUpdates": "Greška pri provjeri ažuriranja", "errorCheckingUpdates": "Greška pri provjeri ažuriranja",
"errorCheckingUpdatesNotifDescription": "Obavijest koja se prikazuje kada provjera sigurnosnog ažuriranja ne uspije", "errorCheckingUpdatesNotifDescription": "Obavijest koja se prikazuje kada provjera sigurnosnog ažuriranja ne uspije",
"appsRemoved": "Aplikacije su uklonjene", "appsRemoved": "Aplikacije su uklonjene",
"appsRemovedNotifDescription": "Obavještava korisnika da je jedna ili više aplikacija uklonjeno zbog grešaka prilikom učitavanja", "appsRemovedNotifDescription": "Obavještava korisnika da je jedna ili više aplikacija uklonjeno zbog grešaka prilikom učitavanja",
"xWasRemovedDueToErrorY": "{} je uklonjen zbog ove greške: {}", "xWasRemovedDueToErrorY": "{} je uklonjen zbog ove greške: {}",
"completeAppInstallation": "Dovršite instalaciju aplikacije", "completeAppInstallation": "Dovršite instalaciju aplikacije",
"obtainiumMustBeOpenToInstallApps": "Obtainium mora biti otvoren za instalaciju aplikacija", "obtainiumMustBeOpenToInstallApps": "Obtainium mora biti otvoren za instalaciju aplikacija",
"completeAppInstallationNotifDescription": "Traži od korisnika da se vrati u Obtainium kako bi dovršio instalaciju aplikacije", "completeAppInstallationNotifDescription": "Traži od korisnika da se vrati u Obtainium kako bi dovršio instalaciju aplikacije",
"checkingForUpdates": "Tražim moguće nadogradnje", "checkingForUpdates": "Tražim moguće nadogradnje",
"checkingForUpdatesNotifDescription": "Privremeno obavještenje koje se pojavljuje prilikom provjere ažuriranja", "checkingForUpdatesNotifDescription": "Privremeno obavještenje koje se pojavljuje prilikom provjere ažuriranja",
"pleaseAllowInstallPerm": "Dozvolite Obtainiumu da instalira aplikacije", "pleaseAllowInstallPerm": "Dozvolite Obtainiumu da instalira aplikacije",
"trackOnly": "Samo za praćenje", "trackOnly": "Samo za praćenje",
"errorWithHttpStatusCode": "Greška {}", "errorWithHttpStatusCode": "Greška {}",
"versionCorrectionDisabled": "Ispravka verzije je onemogućena (izgleda da plugin ne radi)", "versionCorrectionDisabled": "Ispravka verzije je onemogućena (izgleda da plugin ne radi)",
"unknown": "Nepoznato", "unknown": "Nepoznato",
"none": "Ništa", "none": "Ništa",
"never": "Nikad", "never": "Nikad",
"latestVersionX": "Najnovija verzija: {}", "latestVersionX": "Najnovija verzija: {}",
"installedVersionX": "Instalirana verzija: {}", "installedVersionX": "Instalirana verzija: {}",
"lastUpdateCheckX": "Posljednja provjera ažuriranja: {}", "lastUpdateCheckX": "Posljednja provjera ažuriranja: {}",
"remove": "Izbriši", "remove": "Izbriši",
"yesMarkUpdated": "Da, označi kao ažurirano", "yesMarkUpdated": "Da, označi kao ažurirano",
"fdroid": "F-Droid Official", "fdroid": "F-Droid Official",
"appIdOrName": "ID ili ime aplikacije", "appIdOrName": "ID ili ime aplikacije",
"appId": "Apl ID", "appId": "Apl ID",
"appWithIdOrNameNotFound": "Nije pronađena aplikacija s tim ID-om ili imenom", "appWithIdOrNameNotFound": "Nije pronađena aplikacija s tim ID-om ili imenom",
"reposHaveMultipleApps": "Repo-i mogu sadržavati više aplikacija", "reposHaveMultipleApps": "Repo-i mogu sadržavati više aplikacija",
"fdroidThirdPartyRepo": "F-Droid Repo treće strane", "fdroidThirdPartyRepo": "F-Droid Repo treće strane",
"steam": "Steam", "steam": "Steam",
"steamMobile": "Steam Mobile", "steamMobile": "Steam Mobile",
"steamChat": "Razgovor na Steamu (chat)", "steamChat": "Razgovor na Steamu (chat)",
"install": "Instaliraj", "install": "Instaliraj",
"markInstalled": "Označi kao instalirano", "markInstalled": "Označi kao instalirano",
"update": "Nadogradi", "update": "Nadogradi",
"markUpdated": "Označi kao ažurirano", "markUpdated": "Označi kao ažurirano",
"additionalOptions": "Dodatne opcije", "additionalOptions": "Dodatne opcije",
"disableVersionDetection": "Onemogući detekciju verzije", "disableVersionDetection": "Onemogući detekciju verzije",
"noVersionDetectionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno.", "noVersionDetectionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno.",
"downloadingX": "Preuzimanje {}", "downloadingX": "Preuzimanje {}",
"downloadNotifDescription": "Obavještava korisnika o napretku u preuzimanju aplikacije", "downloadNotifDescription": "Obavještava korisnika o napretku u preuzimanju aplikacije",
"noAPKFound": "APK nije pronađen", "noAPKFound": "APK nije pronađen",
"noVersionDetection": "Nema detekcije verzije", "noVersionDetection": "Nema detekcije verzije",
"categorize": "Kategoriziraj", "categorize": "Kategoriziraj",
"categories": "Kategorije", "categories": "Kategorije",
"category": "Kategorija", "category": "Kategorija",
"noCategory": "Nema kategorije", "noCategory": "Nema kategorije",
"noCategories": "Nema kategorija", "noCategories": "Nema kategorija",
"deleteCategoriesQuestion": "Želite li izbrisati kategorije?", "deleteCategoriesQuestion": "Želite li izbrisati kategorije?",
"categoryDeleteWarning": "Sve aplikacije u izbrisanim kategorijama će biti postavljene kao nekategorisane.", "categoryDeleteWarning": "Sve aplikacije u izbrisanim kategorijama će biti postavljene kao nekategorisane.",
"addCategory": "Dodaj kategoriju", "addCategory": "Dodaj kategoriju",
"label": "Oznaka", "label": "Oznaka",
"language": "Jezik", "language": "Jezik",
"copiedToClipboard": "Podaci kopirani u međuspremnik", "copiedToClipboard": "Podaci kopirani u međuspremnik",
"storagePermissionDenied": "Dozvola za pohranu je odbijena", "storagePermissionDenied": "Dozvola za pohranu je odbijena",
"selectedCategorizeWarning": "Ovo će zamijeniti sve postojeće postavke kategorije za odabrane aplikacije.", "selectedCategorizeWarning": "Ovo će zamijeniti sve postojeće postavke kategorije za odabrane aplikacije.",
"filterAPKsByRegEx": "Filtrirajte APK-ove prema regularnom izrazu", "filterAPKsByRegEx": "Filtrirajte APK-ove prema regularnom izrazu",
"removeFromObtainium": "Ukloni iz Obtainiuma", "removeFromObtainium": "Ukloni iz Obtainiuma",
"uninstallFromDevice": "Deinstaliraj s uređaja", "uninstallFromDevice": "Deinstaliraj s uređaja",
"onlyWorksWithNonVersionDetectApps": "Radi samo za aplikacije s onemogućenom detekcijom verzije.", "onlyWorksWithNonVersionDetectApps": "Radi samo za aplikacije s onemogućenom detekcijom verzije.",
"releaseDateAsVersion": "Koristi datum izdanja kao verziju", "releaseDateAsVersion": "Koristi datum izdanja kao verziju",
"releaseDateAsVersionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno, ali je datum izdavanja dostupan.", "releaseDateAsVersionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno, ali je datum izdavanja dostupan.",
"changes": "Promjene", "changes": "Promjene",
"releaseDate": "Datum izdavanja", "releaseDate": "Datum izdavanja",
"importFromURLsInFile": "Uvoz iz URL-ova u datoteci (kao što je OPML)", "importFromURLsInFile": "Uvoz iz URL-ova u datoteci (kao što je OPML)",
"versionDetection": "Otkrivanje verzije", "versionDetection": "Otkrivanje verzije",
"standardVersionDetection": "Detekcija standardne verzije", "standardVersionDetection": "Detekcija standardne verzije",
"groupByCategory": "Grupiši po kategoriji", "groupByCategory": "Grupiši po kategoriji",
"autoApkFilterByArch": "Pokušajte filtrirati APK-ove po arhitekturi procesora ako je moguće", "autoApkFilterByArch": "Pokušajte filtrirati APK-ove po arhitekturi procesora ako je moguće",
"overrideSource": "Premosti izvor", "overrideSource": "Premosti izvor",
"dontShowAgain": "Ne prikazuj ovo ponovo", "dontShowAgain": "Ne prikazuj ovo ponovo",
"dontShowTrackOnlyWarnings": "Ne prikazuj upozorenja „Samo za praćenje”", "dontShowTrackOnlyWarnings": "Ne prikazuj upozorenja „Samo za praćenje”",
"dontShowAPKOriginWarnings": "Ne prikazuj upozorenja o porijeklu APK-a", "dontShowAPKOriginWarnings": "Ne prikazuj upozorenja o porijeklu APK-a",
"moveNonInstalledAppsToBottom": "Premjesti neinstalirane aplikacije na dno prikaza aplikacija", "moveNonInstalledAppsToBottom": "Premjesti neinstalirane aplikacije na dno prikaza aplikacija",
"gitlabPATLabel": "GitLab token za lični pristup\n(Omogućava pretraživanje i bolje otkrivanje APK-a)", "gitlabPATLabel": "GitLab token za lični pristup\n(Omogućava pretraživanje i bolje otkrivanje APK-a)",
"about": "O nama", "about": "O nama",
"requiresCredentialsInSettings": "Za ovo su potrebni dodatni akreditivi (u Postavkama)", "requiresCredentialsInSettings": "Za ovo su potrebni dodatni akreditivi (u Postavkama)",
"checkOnStart": "Provjerite ima li novosti pri pokretanju", "checkOnStart": "Provjerite ima li novosti pri pokretanju",
"tryInferAppIdFromCode": "Pokušati otkriti ID aplikacije iz izvornog koda", "tryInferAppIdFromCode": "Pokušati otkriti ID aplikacije iz izvornog koda",
"removeOnExternalUninstall": "Automatski ukloni eksterno deinstalirane aplikacije", "removeOnExternalUninstall": "Automatski ukloni eksterno deinstalirane aplikacije",
"pickHighestVersionCode": "Automatski odaberite najviši kôd verzije APK-a", "pickHighestVersionCode": "Automatski odaberite najviši kôd verzije APK-a",
"checkUpdateOnDetailPage": "Provjerite ima li novosti pri otvaranju stranice s detaljima aplikacije", "checkUpdateOnDetailPage": "Provjerite ima li novosti pri otvaranju stranice s detaljima aplikacije",
"disablePageTransitions": "Ugasite animaciju prijelaza stranice", "disablePageTransitions": "Ugasite animaciju prijelaza stranice",
"reversePageTransitions": "Reverzne animacije prijelaza stranice", "reversePageTransitions": "Reverzne animacije prijelaza stranice",
"minStarCount": "Minimum Star Count", "minStarCount": "Minimum Star Count",
"addInfoBelow": "Add this info below.", "addInfoBelow": "Add this info below.",
"addInfoInSettings": "Add this info in the Settings.", "addInfoInSettings": "Add this info in the Settings.",
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.", "githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.", "gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
"sortByFileNamesNotLinks": "Sort by file names instead of full links", "sortByFileNamesNotLinks": "Sort by file names instead of full links",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression", "filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')", "customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
"appsPossiblyUpdated": "App Updates Attempted", "appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background", "appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.", "xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.", "enableBackgroundUpdates": "Enable background updates",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.", "backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
"verifyLatestTag": "Verify the 'latest' tag", "backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)", "verifyLatestTag": "Verify the 'latest' tag",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi", "intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK", "intermediateLinkNotFound": "Intermediate link not found",
"versionExtractionRegEx": "Version Extraction RegEx", "exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"matchGroupToUse": "Match Group to Use", "bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"highlightTouchTargets": "Highlight less obvious touch targets", "autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"pickExportDir": "Pick Export Directory", "versionExtractionRegEx": "Version Extraction RegEx",
"autoExportOnChanges": "Auto-export on changes", "matchGroupToUse": "Match Group to Use",
"filterVersionsByRegEx": "Filter Versions by Regular Expression", "highlightTouchTargets": "Highlight less obvious touch targets",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK", "pickExportDir": "Pick Export Directory",
"removeAppQuestion": { "autoExportOnChanges": "Auto-export on changes",
"one": "Želite li ukloniti aplikaciju?", "filterVersionsByRegEx": "Filter Versions by Regular Expression",
"other": "Želite li ukloniti aplikacije?" "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
}, "dontSortReleasesList": "Retain release order from API",
"tooManyRequestsTryAgainInMinutes": { "reverseSort": "Reverse sorting",
"one": "Previše zahtjeva (ograničena broj zahteva) - pokušajte ponovo za {} minutu", "debugMenu": "Debug Menu",
"other": "Previše zahtjeva (ograničena cijena) - pokušajte ponovo za {} min." "bgTaskStarted": "Background task started - check logs.",
}, "runBgCheckNow": "Run Background Update Check Now",
"bgUpdateGotErrorRetryInMinutes": { "versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"one": "Provjera ažuriranja u pozadini naišla je na {}, zakazuje se ponovni pokušaj za {} minutu", "installing": "Installing",
"other": "Provjera ažuriranja u pozadini naišla je na {}, zakazuje se ponovni pokušaj za {} min." "skipUpdateNotifications": "Skip update notifications",
}, "updatesAvailableNotifChannel": "Dostupna ažuriranja",
"bgCheckFoundUpdatesWillNotifyIfNeeded": { "appsUpdatedNotifChannel": "Aplikacije su ažurirane",
"one": "Provjera ažuriranja u pozadini je pronašla {} ažuriranje - korisnik će biti obavješten ako je to potrebno", "appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
"other": "Provjera ažuriranja u pozadini je pronašla {} ažuriranja - korisnik će biti obavješten ako je to potrebno" "errorCheckingUpdatesNotifChannel": "Greška pri provjeri ažuriranja",
}, "appsRemovedNotifChannel": "Aplikacije su uklonjene",
"apps": { "downloadingXNotifChannel": "Preuzimanje {}",
"one": "{} aplikacija", "completeAppInstallationNotifChannel": "Dovršite instalaciju aplikacije",
"other": "{} aplikacije" "checkingForUpdatesNotifChannel": "Tražim moguće nadogradnje",
}, "removeAppQuestion": {
"url": { "one": "Želite li ukloniti aplikaciju?",
"one": "{} URL", "other": "Želite li ukloniti aplikacije?"
"other": "{} URL-ovi" },
}, "tooManyRequestsTryAgainInMinutes": {
"minute": { "one": "Previše zahtjeva (ograničena broj zahteva) - pokušajte ponovo za {} minutu",
"one": "{} minuta", "other": "Previše zahtjeva (ograničena cijena) - pokušajte ponovo za {} min."
"other": "min." },
}, "bgUpdateGotErrorRetryInMinutes": {
"hour": { "one": "Provjera ažuriranja u pozadini naišla je na {}, zakazuje se ponovni pokušaj za {} minutu",
"one": "{} sat", "other": "Provjera ažuriranja u pozadini naišla je na {}, zakazuje se ponovni pokušaj za {} min."
"other": "{} sat/i" },
}, "bgCheckFoundUpdatesWillNotifyIfNeeded": {
"day": { "one": "Provjera ažuriranja u pozadini je pronašla {} ažuriranje - korisnik će biti obavješten ako je to potrebno",
"one": "{} dan", "other": "Provjera ažuriranja u pozadini je pronašla {} ažuriranja - korisnik će biti obavješten ako je to potrebno"
"other": "{} dana" },
}, "apps": {
"clearedNLogsBeforeXAfterY": { "one": "{} aplikacija",
"one": "Izbrisan {n} log (prije = {before}, nakon = {after})", "other": "{} aplikacije"
"other": "Izbrisano {n} log-ova (prije = {before}, nakon = {after})" },
}, "url": {
"xAndNMoreUpdatesAvailable": { "one": "{} URL",
"one": "{} i još 1 aplikacija ima ažuriranja.", "other": "{} URL-ovi"
"other": "{} i još {} aplikacija imaju ažuriranja." },
}, "minute": {
"xAndNMoreUpdatesInstalled": { "one": "{} minuta",
"one": "{} i još 1 aplikacija je ažurirana.", "other": "min."
"other": "{} i još {} aplikacija je ažurirano." },
}, "hour": {
"xAndNMoreUpdatesPossiblyInstalled": { "one": "{} sat",
"one": "{} and 1 more app may have been updated.", "other": "{} sat/i"
"other": "{} and {} more apps may have been updated." },
} "day": {
} "one": "{} dan",
"other": "{} dana"
},
"clearedNLogsBeforeXAfterY": {
"one": "Izbrisan {n} log (prije = {before}, nakon = {after})",
"other": "Izbrisano {n} log-ova (prije = {before}, nakon = {after})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} i još 1 aplikacija ima ažuriranja.",
"other": "{} i još {} aplikacija imaju ažuriranja."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} i još 1 aplikacija je ažurirana.",
"other": "{} i još {} aplikacija je ažurirano."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated."
}
}

329
assets/translations/cs.json Normal file
View File

@ -0,0 +1,329 @@
{
"invalidURLForSource": "Žádná platná {} adresa URL aplikace",
"noReleaseFound": "Nebyla nalezena odpovídající verze",
"noVersionFound": "Nelze určit verzi vydání",
"urlMatchesNoSource": "URL neodpovídá žádnému známému zdroji",
"cantInstallOlderVersion": "Nelze nainstalovat starší verzi aplikace",
"appIdMismatch": "ID staženého balíčku neodpovídá ID existující aplikace",
"functionNotImplemented": "Tato třída nemá implementovánu tuto funkci",
"placeholder": "Zástupce",
"someErrors": "Vyskytly se nějaké chyby",
"unexpectedError": "Neočekávaná chyba",
"ok": "Okay",
"and": "a",
"githubPATLabel": "GitHub Personal Access Token (Raises Rate Limit)",
"includePrereleases": "includepreleases",
"fallbackToOlderReleases": "Fallback to older releases",
"filterReleaseTitlesByRegEx": "Názvy vydání podle regulárního výrazu\filtr",
"invalidRegEx": "Neplatný regulární výraz",
"noDescription": "Žádný popis",
"cancel": "Zrušit",
"continue": "Pokračovat",
"requiredInBracets": "(Required)",
"dropdownNoOptsError": "ERROR: DROPDOWN MUSÍ MÍT AŽ JEDNU MOŽNOST",
"color": "barva",
"githubStarredRepos": "GitHub Starred Repos",
"uname": "username",
"wrongArgNum": "Špatný počet předložených argumentů",
"xIsTrackOnly": "{} je určeno pouze pro sledování",
"source": "zdroj",
"app": "App",
"appsFromSourceAreTrackOnly": "Aplikace z tohoto zdroje jsou 'Jen sledovány'.",
"youPickedTrackOnly": "Vybrali jste možnost 'Jen sledovat'.",
"trackOnlyAppDescription": "Aplikace je sledována kvůli aktualizacím, ale Obtainium ji nebude stahovat ani instalovat.",
"cancelled": "Zrušeno",
"appAlreadyAdded": "Aplikace již přidána",
"alreadyUpToDateQuestion": "App already up to date?",
"addApp": "Přidat aplikaci",
"appSourceURL": "zdrojová adresa URL aplikace",
"error": "Chyba",
"add": "Přidat",
"searchSomeSourcesLabel": "Vyhledávání (pouze konkrétní zdroje)",
"search": "Hledat",
"additionalOptsFor": "Další možnosti pro {}",
"supportedSources": "Podporované zdroje",
"trackOnlyInBrackets": "(Pouze stopy)",
"searchableInBrackets": "(s možností vyhledávání)",
"appsString": "Apky",
"noApps": "Žádné aplikace",
"noAppsForFilter": "žádné aplikace pro vybraný filtr",
"byX": "By {}",
"percentProgress": "Pokrok: {}%",
"pleaseWait": "Počkejte prosím",
"updateAvailable": "Aktualizace je k dispozici",
"estimateInBracketsShort": "(approx.)",
"notInstalled": "Není nainstalováno",
"estimateInBrackets": "(přibližně)",
"selectAll": "Vybrat Vše",
"deselectN": "{} deselected",
"xWillBeRemovedButRemainInstalled": "{} bude odstraněn z Obtainium, ale zůstane nainstalován v zařízení.",
"removeSelectedAppsQuestion": "Odebrat vybrané aplikace?",
"removeSelectedApps": "Odebrat vybrané aplikace",
"updateX": "Aktualizovat {}",
"installX": "Instalovat {}",
"markXTrackOnlyAsUpdated": "Označit {}\n(Track-Only)\njako aktualizované",
"changeX": "Změnit {}",
"installUpdateApps": "Instalovat/aktualizovat aplikace",
"installUpdateSelectedApps": "Instalovat/aktualizovat vybrané aplikace",
"markXSelectedAppsAsUpdated": "označit {} vybrané aplikace jako aktuální?",
"no": "Ne",
"yes": "ano",
"markSelectedAppsUpdated": "označit vybrané aplikace jako aktuální",
"pinToTop": "Připnout nahoru",
"unpinFromTop": "'Unpin Top'",
"resetInstallStatusForSelectedAppsQuestion": "Obnovit stav instalace vybraných aplikací?",
"installStatusOfXWillBeResetExplanation": "Stav instalace vybraných aplikací bude resetován. To může být užitečné, pokud je verze aplikace zobrazená v Obtainium nesprávná z důvodu neúspěšných aktualizací nebo jiných problémů.",
"shareSelectedAppURLs": "Sdílet adresy URL vybraných aplikací",
"resetInstallStatus": "Obnovení stavu instalace",
"more": "more",
"removeOutdatedFilter": "Odstranit filtr aplikace 'Not Current'",
"showOutdatedOnly": "Zobrazit pouze aplikace, které nejsou aktuální",
"filter": "Filtr",
"filterActive": "Filtr *",
"filterApps": "Filtrovat aplikace",
"appName": "název aplikace",
"author": "Autor",
"upToDateApps": "Apps with current version",
"nonInstalledApps": "Apps not installed",
"importExport": "Import/Export",
"settings": "Nastavení",
"exportedTo": "Exportováno do {}",
"obtainiumExport": "Obtainium Export",
"invalidInput": "Neplatný vstup",
"importedX": "Importováno {}",
"obtainiumImport": "Obtainium Import",
"importFromURLList": "Import ze seznamu URL",
"searchQuery": "Search Query",
"appURLList": "App URL List",
"line": "line",
"searchX": "Search {}",
"noResults": "Nebyly nalezeny žádné výsledky",
"importX": "Import {}",
"importedAppsIdDisclaimer": "Importované aplikace mohou být nesprávně zobrazeny jako \"Neinstalované\". Chcete-li to opravit, nainstalujte je znovu prostřednictvím Obtainium. To nemá vliv na data aplikací. Ovlivňuje pouze metody importu URL a třetích stran.",
"importErrors": "Import Errors",
"importedXOfYApps": "{}importováno {}aplikací.",
"followingURLsHadErrors": "U následujících adres URL došlo k chybám:",
"okay": "Okay",
"selectURL": "Select URL",
"selectURLs": "Select URLs",
"pick": "Vybrat",
"theme": "Téma",
"dark": "Tmavé",
"light": "Světlé",
"followSystem": "Follow System",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "Použít čistě černé tmavé téma",
"appSortBy": "Seřadit aplikaci podle",
"authorName": "autor/jméno",
"nameAuthor": "jméno/autor",
"asAdded": "AsAdded",
"appSortOrder": "Sort App By",
"ascending": "Vzestupně",
"descending": "Sestupně",
"bgUpdateCheckInterval": "Background Update Check Interval",
"neverManualOnly": "Nikdy - pouze ručně",
"appearance": "Vzhled",
"showWebInAppView": "Zobrazit zdrojové webové stránky v zobrazení aplikace",
"pinUpdates": "Připnout aplikace s aktualizacemi nahoře",
"updates": "Updates",
"sourceSpecific": "source specific",
"appSource": "zdroj aplikace",
"noLogs": "Žádné protokoly",
"appLogs": "App Logs",
"close": "Zavřít",
"share": "Sdílet",
"appNotFound": "App not found",
"obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "Vybrat APK",
"appHasMoreThanOnePackage": "{} má více než jeden balíček:",
"deviceSupportsXArch": "Vaše zařízení podporuje architekturu CPU {}.",
"deviceSupportsFollowingArchs": "Vaše zařízení podporuje následující architektury CPU:",
"warning": "Varování",
"sourceIsXButPackageFromYPrompt": "The app source is '{}' but the release package is from '{}'. Pokračovat?",
"updatesAvailable": "dostupné aktualizace",
"updatesAvailableNotifDescription": "Upozorňuje uživatele, že jsou k dispozici aktualizace pro jednu nebo více aplikací sledovaných Obtainium",
"noNewUpdates": "Žádné nové aktualizace.",
"xHasAnUpdate": "{} má aktualizaci.",
"appsUpdated": "Aplikace aktualizovány",
"appsUpdatedNotifDescription": "Upozorňuje uživatele, že byly provedeny aktualizace jedné nebo více aplikací na pozadí",
"xWasUpdatedToY": "{} byl aktualizován na {}",
"errorCheckingUpdates": "Chybová kontrola aktualizací",
"errorCheckingUpdatesNotifDescription": "Oznámení zobrazené při neúspěšné kontrole aktualizací na pozadí",
"appsRemoved": "Odstraněné aplikace",
"appsRemovedNotifDescription": "Oznámení uživateli, že jedna nebo více aplikací byly odstraněny z důvodu chyb při načítání",
"xWasRemovedDueToErrorY": "{} byla odstraněna z důvodu následující chyby: {}",
"completeAppInstallation": "Dokončit instalaci aplikace",
"obtainiumMustBeOpenToInstallApps": "Obtainium musí být otevřeno, aby bylo možné instalovat aplikace",
"completeAppInstallationNotifDescription": "Vyzvat uživatele k návratu do Obtainium pro dokončení instalace aplikací",
"checkingForUpdates": "Zkontrolovat aktualizace",
"checkingForUpdatesNotifDescription": "Dočasné oznámení zobrazené při kontrole aktualizací",
"pleaseAllowInstallPerm": "Povolte prosím Obtainium instalovat aplikace",
"trackOnly": "Jen sledovat",
"errorWithHttpStatusCode": "error {}",
"versionCorrectionDisabled": "Oprava verze zakázána (zásuvný modul zřejmě nefunguje)",
"unknown": "Unknown",
"none": "None",
"never": "Nikdy",
"latestVersionX": "Nejnovější verze: {}",
"installedVersionX": "Nainstalovaná verze: {}",
"lastUpdateCheckX": "Poslední kontrola aktualizace: {}",
"remove": "Odebrat",
"yesMarkUpdated": "Ano, označit jako aktualizované",
"fdroid": "F-Droid Official",
"appIdOrName": "App ID or Name",
"appId": "App ID",
"appWithIdOrNameNotFound": "Žádná aplikace s tímto ID nebo názvem nebyla nalezena",
"reposHaveMultipleApps": "Repozitáře mohou obsahovat více aplikací",
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
"steam": "Steam",
"steamMobile": "Steam Mobile",
"steamChat": "Steam Chat",
"install": "Install",
"markInstalled": "Označit jako nainstalovaný",
"update": "Aktualizovat",
"markUpdated": "Označit jako aktuální",
"additionalOptions": "Additional Options",
"disableVersionDetection": "Zakázat detekci verze",
"noVersionDetectionExplanation": "Tato volba by měla být použita pouze u aplikací, kde detekce verzí nefunguje správně.",
"downloadingX": "download {}",
"downloadNotifDescription": "Informuje uživatele o průběhu stahování aplikace",
"noAPKFound": "Žádná APK nebyla nalezena",
"noVersionDetection": "Žádná detekce verze",
"categorize": "Kategorizovat",
"categories": "Kategorie",
"category": "kategorie",
"noCategory": "Žádná kategorie",
"noCategories": "Žádné kategorie",
"deleteCategoriesQuestion": "Smazat kategorie?",
"categoryDeleteWarning": "Všechny aplikace v odstraněných kategoriích budou nastaveny na nekategorizované.",
"addCategory": "přidat kategorii",
"label": "štítek",
"language": "Jazyk",
"copiedToClipboard": "zkopírováno do schránky",
"storagePermissionDenied": "povolení k ukládání odepřeno",
"selectedCategorizeWarning": "Toto nahradí všechna stávající nastavení kategorií pro vybrané aplikace.",
"filterAPKsByRegEx": "Filtrovat APK podle regulárního výrazu",
"removeFromObtainium": "Odebrat z Obtainium",
"uninstallFromDevice": "Odinstalovat ze zařízení",
"onlyWorksWithNonVersionDetectApps": "Funguje pouze pro aplikace s vypnutou detekcí verze.",
"releaseDateAsVersion": "Použít datum vydání jako verzi",
"releaseDateAsVersionExplanation": "Tato možnost by měla být použita pouze u aplikací, u kterých detekce verze nefunguje správně, ale je k dispozici datum vydání.",
"changes": "Změny",
"releaseDate": "datum vydání",
"importFromURLsInFile": "Importovat adresy URL ze souboru (např. OPML)",
"versionDetection": "detekce verze",
"standardVersionDetection": "standardní detekce verze",
"groupByCategory": "Seskupit podle kategorie",
"autoApkFilterByArch": "Pokud je to možné, pokuste se filtrovat soubory APK podle architektury procesoru",
"overrideSource": "Přepsat zdroj",
"dontShowAgain": "Nezobrazovat znovu",
"dontShowTrackOnlyWarnings": "Nezobrazovat varování pro 'Track Only'",
"dontShowAPKOriginWarnings": "Nezobrazovat varování pro původ APK",
"moveNonInstalledAppsToBottom": "Přesunout nenainstalované aplikace na konec zobrazení Aplikace",
"gitlabPATLabel": "GitLab Personal Access Token\n(Umožňuje vyhledávání a lepší zjišťování APK)",
"about": "About",
"requiresCredentialsInSettings": "Vyžaduje další pověření (v nastavení)",
"checkOnStart": "Zkontrolovat jednou při spuštění",
"tryInferAppIdFromCode": "Pokusit se určit ID aplikace ze zdrojového kódu",
"removeOnExternalUninstall": "Automaticky odstranit externě odinstalované aplikace",
"pickHighestVersionCode": "Automaticky vybrat APK s kódem nejvyšší verze",
"checkUpdateOnDetailPage": "Zkontrolovat aktualizace při otevření stránky s podrobnostmi aplikace",
"disablePageTransitions": "Zakázat animace pro přechody stránek",
"reversePageTransitions": "Obrátit animace pro přechody stránek",
"minStarCount": "Minimální počet hvězdiček",
"addInfoBelow": "Přidat tuto informaci na konec stránky",
"addInfoInSettings": "Přidat tuto informaci do nastavení.",
"githubSourceNote": "Omezení rychlosti GitHub lze obejít pomocí klíče API.",
"gitlabSourceNote": "Extrakce GitLab APK nemusí fungovat bez klíče API",
"sortByFileNamesNotLinks": "Řadit podle názvů souborů místo celých odkazů",
"filterReleaseNotesByRegEx": "Filtrovat poznámky k vydání podle regulárního výrazu",
"customLinkFilterRegex": "Vlastní filtr odkazů APK podle regulárního výrazu (výchozí '.apk$')",
"appsPossiblyUpdated": "Byly provedeny pokusy o aktualizaci aplikací",
"appsPossiblyUpdatedNotifDescription": "Upozorňuje uživatele, že na pozadí mohly být provedeny aktualizace jedné nebo více aplikací",
"xWasPossiblyUpdatedToY": "{} mohlo být aktualizováno na {}.",
"enableBackgroundUpdates": "Povolit aktualizace na pozadí",
"backgroundUpdateReqsExplanation": "Aktualizace na pozadí nemusí být možné pro všechny aplikace.",
"backgroundUpdateLimitsExplanation": "Úspěšnost instalace na pozadí lze určit pouze v případě, že je otevřen Obtainium.",
"verifyLatestTag": "Ověřit značku 'latest'",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
"intermediateLinkNotFound": "Intermediate link not found",
"exemptFromBackgroundUpdates": "Vyloučit aktualizace na pozadí (pokud jsou povoleny)",
"bgUpdatesOnWiFiOnly": "Zakázat aktualizace na pozadí, pokud není přítomna Wi-Fi",
"autoSelectHighestVersionCode": "Automatický výběr nejvyššího kódu verze APK",
"versionExtractionRegEx": "Version Extraction RegEx",
"matchGroupToUse": "Match Group to Use",
"highlightTouchTargets": "Zvýraznit méně zjevné cíle dotyku",
"pickExportDir": "Vybrat adresář pro export",
"autoExportOnChanges": "Automatický export při změnách",
"filterVersionsByRegEx": "Filtrovat verze podle regulárního výrazu",
"trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovaný kód verze APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"updatesAvailableNotifChannel": "dostupné aktualizace",
"appsUpdatedNotifChannel": "Aplikace aktualizovány",
"appsPossiblyUpdatedNotifChannel": "Byly provedeny pokusy o aktualizaci aplikací",
"errorCheckingUpdatesNotifChannel": "Chybová kontrola aktualizací",
"appsRemovedNotifChannel": "Odstraněné aplikace",
"downloadingXNotifChannel": "download {}",
"completeAppInstallationNotifChannel": "Dokončit instalaci aplikace",
"checkingForUpdatesNotifChannel": "Zkontrolovat aktualizace",
"removeAppQuestion": {
"one": "Odstranit Apku?",
"other": "Odstranit Apky?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "Příliš mnoho požadavků (omezená rychlost) - zkuste to znovu za {} minutu",
"other": "Příliš mnoho požadavků (omezená rychlost) - zkuste to znovu za {} minut"
},
"bgUpdateGotErrorRetryInMinutes": {
"one": "Při kontrole aktualizace na pozadí byla zjištěna chyba {}, opakování pokusu bude naplánováno za {} minut",
"other": "Během kontroly aktualizace na pozadí byla zjištěna chyba {}, opakování bude naplánováno za {} minut"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "Při kontrole aktualizací na pozadí nalezena {}aktualizace - v případě potřeby upozorní uživatele",
"other": "Kontrola aktualizací na pozadí našla {} aktualizací - v případě potřeby upozorní uživatele"
},
"apps": {
"one": "{} App",
"other": "{} apps"
},
"url": {
"jedna": "{} URL",
"other": "{} URLs"
},
"minute": {
"one": "{} minute",
"other": "{} minutes"
},
"hour": {
"jedna": "{} hodina",
"other": "{} hours"
},
"day": {
"jedna": "{} den",
"other": "{} dny"
},
"clearedNLogsBeforeXAfterY": {
"one": "{n} log vymazán (před = {před}, po = {po})",
"other": "{n} logů vymazáno (před = {před}, po = {po})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} a 1 další aplikace mají aktualizace.",
"other": "{} a {} další aplikace mají aktualizace."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} a {} další aplikace mají aktualizace.",
"další": "{} a {} další aplikace byly aktualizovány."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} a {} další aplikace byly možná aktualizovány",
"other": "{} a {} další aplikace mohly být aktualizovány."
}
}

View File

@ -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",
@ -170,7 +170,7 @@
"lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}", "lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}",
"remove": "Entfernen", "remove": "Entfernen",
"yesMarkUpdated": "Ja, als aktualisiert markieren", "yesMarkUpdated": "Ja, als aktualisiert markieren",
"fdroid": "F-Droid Official", "fdroid": "offizielles F-Droid-Repo",
"appIdOrName": "App ID oder Name", "appIdOrName": "App ID oder Name",
"appId": "App ID", "appId": "App ID",
"appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden", "appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden",
@ -242,34 +242,53 @@
"appsPossiblyUpdated": "App Aktualisierungen wurden versucht", "appsPossiblyUpdated": "App Aktualisierungen wurden versucht",
"appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Updates für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden", "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 {}.", "xWasPossiblyUpdatedToY": "{} wurde möglicherweise aktualisiert auf {}.",
"enableBackgroundUpdates": "Aktiviere Hintergrundaktualisierungen",
"backgroundUpdateReqsExplanation": "Die Hintergrundaktualisierung ist möglicherweise nicht für alle Apps möglich.", "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.", "backgroundUpdateLimitsExplanation": "Der Erfolg einer Hintergrundinstallation kann nur festgestellt werden, wenn Obtainium geöffnet wird.",
"verifyLatestTag": "Überprüfe das „latest“ Tag", "verifyLatestTag": "Überprüfe das „latest“ Tag",
"intermediateLinkRegex": "Filter für einen „Zwischen“-Link, der zuerst besucht werden soll",
"intermediateLinkNotFound": "„Zwischen“link nicht gefunden",
"exemptFromBackgroundUpdates": "Ausschluss von Hintergrundaktualisierungen (falls aktiviert)", "exemptFromBackgroundUpdates": "Ausschluss von Hintergrundaktualisierungen (falls aktiviert)",
"bgUpdatesOnWiFiOnly": "Hintergrundaktualisierungen deaktivieren, wenn kein WLAN vorhanden ist", "bgUpdatesOnWiFiOnly": "Hintergrundaktualisierungen deaktivieren, wenn kein WLAN vorhanden ist",
"autoSelectHighestVersionCode": "Automatisch höchste APK-Code-Version auswählen", "autoSelectHighestVersionCode": "Automatisch höchste APK-Code-Version auswählen",
"versionExtractionRegEx": "Versions-Extraktion RegEx", "versionExtractionRegEx": "Versions-Extraktion per RegEx",
"matchGroupToUse": "Zu verwendende Gruppe abgleichen", "matchGroupToUse": "Zu verwendende Gruppe abgleichen",
"highlightTouchTargets": "Weniger offensichtliche Ziele hervorheben", "highlightTouchTargets": "Weniger offensichtliche Ziele hervorheben",
"pickExportDir": "Export-Verzeichnis wählen", "pickExportDir": "Export-Verzeichnis wählen",
"autoExportOnChanges": "Automatischer Export bei Änderung", "autoExportOnChanges": "Automatischer Export bei Änderung",
"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": "Freigaberelease von der API ordern",
"reverseSort": "Umgekehrtes Sortieren",
"debugMenu": "Debug-Menü",
"bgTaskStarted": "Hintergrundaufgabe gestartet Logs prüfen.",
"runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen",
"versionExtractWholePage": "Versions-Extraktion per RegEx auf die gesamte Seite anwenden",
"installing": "Installiere",
"skipUpdateNotifications": "Keine Benachrichtigung zu App-Updates geben",
"updatesAvailableNotifChannel": "Aktualisierungen verfügbar",
"appsUpdatedNotifChannel": "Apps aktualisiert",
"appsPossiblyUpdatedNotifChannel": "App Aktualisierungen wurden versucht",
"errorCheckingUpdatesNotifChannel": "Fehler beim Prüfen auf Aktualisierungen",
"appsRemovedNotifChannel": "Apps entfernt",
"downloadingXNotifChannel": "Lade {} herunter",
"completeAppInstallationNotifChannel": "App Installation abschließen",
"checkingForUpdatesNotifChannel": "Nach Aktualisierungen suchen",
"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",
@ -292,8 +311,8 @@
"other": "{} Tage" "other": "{} Tage"
}, },
"clearedNLogsBeforeXAfterY": { "clearedNLogsBeforeXAfterY": {
"one": "{n} Protokoll gelöscht (vorher = {vorher}, nachher = {nachher})", "one": "{n} Log gelöscht (vorher = {before}, nachher = {after})",
"other": "{n} Protokolle gelöscht (vorher = {vorher}, nachher = {nachher})" "other": "{n} Logs gelöscht (vorher = {before}, nachher = {after})"
}, },
"xAndNMoreUpdatesAvailable": { "xAndNMoreUpdatesAvailable": {
"one": "{} und 1 weitere App haben Aktualisierungen.", "one": "{} und 1 weitere App haben Aktualisierungen.",

View File

@ -252,12 +252,28 @@
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi", "bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK", "autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"versionExtractionRegEx": "Version Extraction RegEx", "versionExtractionRegEx": "Version Extraction RegEx",
"matchGroupToUse": "Match Group to Use", "matchGroupToUse": "Match Group to Use for Version Extraction Regex",
"highlightTouchTargets": "Highlight less obvious touch targets", "highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory", "pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes", "autoExportOnChanges": "Auto-export on changes",
"filterVersionsByRegEx": "Filter Versions by Regular Expression", "filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK", "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"updatesAvailableNotifChannel": "Updates Available",
"appsUpdatedNotifChannel": "Apps Updated",
"appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
"errorCheckingUpdatesNotifChannel": "Error Checking for Updates",
"appsRemovedNotifChannel": "Apps Removed",
"downloadingXNotifChannel": "Downloading {}",
"completeAppInstallationNotifChannel": "Complete App Installation",
"checkingForUpdatesNotifChannel": "Checking for Updates",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Remove App?", "one": "Remove App?",
"other": "Remove Apps?" "other": "Remove Apps?"
@ -310,4 +326,4 @@
"one": "{} and 1 more app may have been updated.", "one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated." "other": "{} and {} more apps may have been updated."
} }
} }

View File

@ -242,9 +242,12 @@
"appsPossiblyUpdated": "App Updates Attempted", "appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background", "appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.", "xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"enableBackgroundUpdates": "Enable background updates",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.", "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.", "backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag", "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)", "exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi", "bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK", "autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
@ -255,6 +258,22 @@
"autoExportOnChanges": "Auto-export on changes", "autoExportOnChanges": "Auto-export on changes",
"filterVersionsByRegEx": "Filter Versions by Regular Expression", "filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK", "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"updatesAvailableNotifChannel": "Actualizaciones Disponibles",
"appsUpdatedNotifChannel": "Aplicaciones Actualizadas",
"appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
"errorCheckingUpdatesNotifChannel": "Error Buscando Actualizaciones",
"appsRemovedNotifChannel": "Aplicaciones Eliminadas",
"downloadingXNotifChannel": "Descargando {}",
"completeAppInstallationNotifChannel": "Instalación Completa de la Aplicación",
"checkingForUpdatesNotifChannel": "Buscando Actualizaciones",
"removeAppQuestion": { "removeAppQuestion": {
"one": "¿Eliminar Aplicación?", "one": "¿Eliminar Aplicación?",
"other": "¿Eliminar Aplicaciones?" "other": "¿Eliminar Aplicaciones?"
@ -307,4 +326,4 @@
"one": "{} and 1 more app may have been updated.", "one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated." "other": "{} and {} more apps may have been updated."
} }
} }

View File

@ -242,9 +242,12 @@
"appsPossiblyUpdated": "App Updates Attempted", "appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background", "appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.", "xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"enableBackgroundUpdates": "Enable background updates",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.", "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.", "backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag", "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)", "exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi", "bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK", "autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
@ -255,6 +258,22 @@
"autoExportOnChanges": "Auto-export on changes", "autoExportOnChanges": "Auto-export on changes",
"filterVersionsByRegEx": "Filter Versions by Regular Expression", "filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK", "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"updatesAvailableNotifChannel": "بروزرسانی در دسترس ",
"appsUpdatedNotifChannel": "برنامه ها به روز شدند",
"appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
"errorCheckingUpdatesNotifChannel": "خطا در بررسی به‌روزرسانی‌ها",
"appsRemovedNotifChannel": "برنامه ها حذف شدند",
"downloadingXNotifChannel": "در حال دانلود {}",
"completeAppInstallationNotifChannel": "نصب کامل برنامه",
"checkingForUpdatesNotifChannel": "بررسی به‌روزرسانی‌ها",
"removeAppQuestion": { "removeAppQuestion": {
"one": "برنامه حذف شود؟", "one": "برنامه حذف شود؟",
"other": "برنامه ها حذف شوند؟" "other": "برنامه ها حذف شوند؟"
@ -307,4 +326,4 @@
"one": "{} and 1 more app may have been updated.", "one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated." "other": "{} and {} more apps may have been updated."
} }
} }

View File

@ -242,9 +242,12 @@
"appsPossiblyUpdated": "App Updates Attempted", "appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background", "appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.", "xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"enableBackgroundUpdates": "Enable background updates",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.", "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.", "backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag", "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)", "exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi", "bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK", "autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
@ -255,6 +258,22 @@
"autoExportOnChanges": "Auto-export on changes", "autoExportOnChanges": "Auto-export on changes",
"filterVersionsByRegEx": "Filter Versions by Regular Expression", "filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK", "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"updatesAvailableNotifChannel": "Mises à jour disponibles",
"appsUpdatedNotifChannel": "Applications mises à jour",
"appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
"errorCheckingUpdatesNotifChannel": "Erreur lors de la vérification des mises à jour",
"appsRemovedNotifChannel": "Applications supprimées",
"downloadingXNotifChannel": "Téléchargement {}",
"completeAppInstallationNotifChannel": "Installation complète de l'application",
"checkingForUpdatesNotifChannel": "Vérification des mises à jour",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Supprimer l'application ?", "one": "Supprimer l'application ?",
"other": "Supprimer les applications ?" "other": "Supprimer les applications ?"
@ -307,4 +326,4 @@
"one": "{} and 1 more app may have been updated.", "one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated." "other": "{} and {} more apps may have been updated."
} }
} }

View File

@ -1,309 +1,329 @@
{ {
"invalidURLForSource": "Érvénytelen a(z) {} app URL-je", "invalidURLForSource": "Érvénytelen a(z) {} app URL-je",
"noReleaseFound": "Nem található megfelelő kiadás", "noReleaseFound": "Nem található megfelelő kiadás",
"noVersionFound": "Nem sikerült meghatározni a kiadás verzióját", "noVersionFound": "Nem sikerült meghatározni a kiadás verzióját",
"urlMatchesNoSource": "Az URL nem egyezik ismert forrással", "urlMatchesNoSource": "Az URL nem egyezik ismert forrással",
"cantInstallOlderVersion": "Nem telepíthető egy app régebbi verziója", "cantInstallOlderVersion": "Nem telepíthető egy app régebbi verziója",
"appIdMismatch": "A letöltött csomagazonosító nem egyezik a meglévő app azonosítóval", "appIdMismatch": "A letöltött csomagazonosító nem egyezik a meglévő app azonosítóval",
"functionNotImplemented": "Ez az osztály nem valósította meg ezt a függvényt", "functionNotImplemented": "Ez az osztály nem valósította meg ezt a függvényt",
"placeholder": "Helykitöltő", "placeholder": "Helykitöltő",
"someErrors": "Néhány hiba történt", "someErrors": "Néhány hiba történt",
"unexpectedError": "Váratlan hiba", "unexpectedError": "Váratlan hiba",
"ok": "Oké", "ok": "Oké",
"and": "és", "and": "és",
"githubPATLabel": "GitHub Personal Access Token (megnöveli a díjkorlátot)", "githubPATLabel": "GitHub Personal Access Token (megnöveli a díjkorlátot)",
"includePrereleases": "Tartalmazza az előzetes kiadásokat", "includePrereleases": "Tartalmazza az előzetes kiadásokat",
"fallbackToOlderReleases": "Visszatérés a régebbi kiadásokhoz", "fallbackToOlderReleases": "Visszatérés a régebbi kiadásokhoz",
"filterReleaseTitlesByRegEx": "A kiadás címeinek szűrése reguláris kifejezéssel", "filterReleaseTitlesByRegEx": "A kiadás címeinek szűrése reguláris kifejezéssel",
"invalidRegEx": "Érvénytelen reguláris kifejezés", "invalidRegEx": "Érvénytelen reguláris kifejezés",
"noDescription": "Nincs leírás", "noDescription": "Nincs leírás",
"cancel": "Mégse", "cancel": "Mégse",
"continue": "Tovább", "continue": "Tovább",
"requiredInBrackets": "(Kötelező)", "requiredInBrackets": "(Kötelező)",
"dropdownNoOptsError": "HIBA: A LEDOBÁST LEGALÁBB EGY OPCIÓHOZ KELL RENDELNI", "dropdownNoOptsError": "HIBA: A LEDOBÁST LEGALÁBB EGY OPCIÓHOZ KELL RENDELNI",
"colour": "Szín", "colour": "Szín",
"githubStarredRepos": "GitHub Csillagos Repo-k", "githubStarredRepos": "GitHub Csillagos Repo-k",
"uname": "Felh.név", "uname": "Felh.név",
"wrongArgNum": "Rossz számú argumentumot adott meg", "wrongArgNum": "Rossz számú argumentumot adott meg",
"xIsTrackOnly": "A(z) {} csak nyomonkövethető", "xIsTrackOnly": "A(z) {} csak nyomonkövethető",
"source": "Forrás", "source": "Forrás",
"app": "App", "app": "App",
"appsFromSourceAreTrackOnly": "Az ebből a forrásból származó alkalmazások 'Csak nyomon követhetőek'.", "appsFromSourceAreTrackOnly": "Az ebből a forrásból származó alkalmazások 'Csak nyomon követhetőek'.",
"youPickedTrackOnly": "A 'Csak követés' opciót választotta.", "youPickedTrackOnly": "A 'Csak követés' opciót választotta.",
"trackOnlyAppDescription": "Az alkalmazás frissítéseit nyomon követi, de az Obtainium nem tudja letölteni vagy telepíteni.", "trackOnlyAppDescription": "Az alkalmazás frissítéseit nyomon követi, de az Obtainium nem tudja letölteni vagy telepíteni.",
"cancelled": "Törölve", "cancelled": "Törölve",
"appAlreadyAdded": "Az app már hozzáadva", "appAlreadyAdded": "Az app már hozzáadva",
"alreadyUpToDateQuestion": "Az app már naprakész?", "alreadyUpToDateQuestion": "Az app már naprakész?",
"addApp": "App hozzáadás", "addApp": "App hozzáadás",
"appSourceURL": "App forrás URL", "appSourceURL": "App forrás URL",
"error": "Hiba", "error": "Hiba",
"add": "Hozzáadás", "add": "Hozzáadás",
"searchSomeSourcesLabel": "Keresés (csak egyes források)", "searchSomeSourcesLabel": "Keresés (csak egyes források)",
"search": "Keresés", "search": "Keresés",
"additionalOptsFor": "További lehetőségek a következőhöz: {}", "additionalOptsFor": "További lehetőségek a következőhöz: {}",
"supportedSources": "Támogatott források", "supportedSources": "Támogatott források",
"trackOnlyInBrackets": "(Csak nyomonkövetés)", "trackOnlyInBrackets": "(Csak nyomonkövetés)",
"searchableInBrackets": "(Kereshető)", "searchableInBrackets": "(Kereshető)",
"appsString": "Appok", "appsString": "Appok",
"noApps": "Nincs App", "noApps": "Nincs App",
"noAppsForFilter": "Nincsenek appok a szűrőhöz", "noAppsForFilter": "Nincsenek appok a szűrőhöz",
"byX": "Fejlesztő: {}", "byX": "Fejlesztő: {}",
"percentProgress": "Folyamat: {}%", "percentProgress": "Folyamat: {}%",
"pleaseWait": "Kis türelmet", "pleaseWait": "Kis türelmet",
"updateAvailable": "Frissítés érhető el", "updateAvailable": "Frissítés érhető el",
"estimateInBracketsShort": "(Becsült)", "estimateInBracketsShort": "(Becsült)",
"notInstalled": "Nem telepített", "notInstalled": "Nem telepített",
"estimateInBrackets": "(Becslés)", "estimateInBrackets": "(Becslés)",
"selectAll": "Mindet kiválaszt", "selectAll": "Mindet kiválaszt",
"deselectN": "Törölje {} kijelölését", "deselectN": "Törölje {} kijelölését",
"xWillBeRemovedButRemainInstalled": "A(z) {} el lesz távolítva az Obtainiumból, de továbbra is telepítve marad az eszközön.", "xWillBeRemovedButRemainInstalled": "A(z) {} el lesz távolítva az Obtainiumból, de továbbra is telepítve marad az eszközön.",
"removeSelectedAppsQuestion": "Eltávolítja a kiválasztott appokat?", "removeSelectedAppsQuestion": "Eltávolítja a kiválasztott appokat?",
"removeSelectedApps": "Távolítsa el a kiválasztott appokat", "removeSelectedApps": "Távolítsa el a kiválasztott appokat",
"updateX": "Frissítés: {}", "updateX": "Frissítés: {}",
"installX": "Telepítés: {}", "installX": "Telepítés: {}",
"markXTrackOnlyAsUpdated": "Jelölje meg: {}\n(Csak nyomon követhető)\nmint Frissített", "markXTrackOnlyAsUpdated": "Jelölje meg: {}\n(Csak nyomon követhető)\nmint Frissített",
"changeX": "Változás {}", "changeX": "Változás {}",
"installUpdateApps": "Appok telepítése/frissítése", "installUpdateApps": "Appok telepítése/frissítése",
"installUpdateSelectedApps": "Telepítse/frissítse a kiválasztott appokat", "installUpdateSelectedApps": "Telepítse/frissítse a kiválasztott appokat",
"markXSelectedAppsAsUpdated": "Megjelöl {} kiválasztott alkalmazást frissítettként?", "markXSelectedAppsAsUpdated": "Megjelöl {} kiválasztott alkalmazást frissítettként?",
"no": "Nem", "no": "Nem",
"yes": "Igen", "yes": "Igen",
"markSelectedAppsUpdated": "Jelölje meg a kiválasztott appokat frissítettként", "markSelectedAppsUpdated": "Jelölje meg a kiválasztott appokat frissítettként",
"pinToTop": "Rögzítés felülre", "pinToTop": "Rögzítés felülre",
"unpinFromTop": "Eltávolít felülről", "unpinFromTop": "Eltávolít felülről",
"resetInstallStatusForSelectedAppsQuestion": "Visszaállítja a kiválasztott appok telepítési állapotát?", "resetInstallStatusForSelectedAppsQuestion": "Visszaállítja a kiválasztott appok telepítési állapotát?",
"installStatusOfXWillBeResetExplanation": "A kiválasztott appok telepítési állapota visszaáll.\n\nEz akkor segíthet, ha az Obtainiumban megjelenített app verzió hibás, frissítések vagy egyéb problémák miatt.", "installStatusOfXWillBeResetExplanation": "A kiválasztott appok telepítési állapota visszaáll.\n\nEz akkor segíthet, ha az Obtainiumban megjelenített app verzió hibás, frissítések vagy egyéb problémák miatt.",
"shareSelectedAppURLs": "Ossza meg a kiválasztott app URL címeit", "shareSelectedAppURLs": "Ossza meg a kiválasztott app URL címeit",
"resetInstallStatus": "Telepítési állapot visszaállítása", "resetInstallStatus": "Telepítési állapot visszaállítása",
"more": "További", "more": "További",
"removeOutdatedFilter": "Távolítsa el az elavult app szűrőt", "removeOutdatedFilter": "Távolítsa el az elavult app szűrőt",
"showOutdatedOnly": "Csak az elavult appok megjelenítése", "showOutdatedOnly": "Csak az elavult appok megjelenítése",
"filter": "Szűrő", "filter": "Szűrő",
"filterActive": "Szűrő *", "filterActive": "Szűrő *",
"filterApps": "Appok szűrése", "filterApps": "Appok szűrése",
"appName": "App név", "appName": "App név",
"author": "Szerző", "author": "Szerző",
"upToDateApps": "Naprakész appok", "upToDateApps": "Naprakész appok",
"nonInstalledApps": "Nem telepített appok", "nonInstalledApps": "Nem telepített appok",
"importExport": "Import/Export", "importExport": "Import/Export",
"settings": "Beállítások", "settings": "Beállítások",
"exportedTo": "Exportálva ide {}", "exportedTo": "Exportálva ide {}",
"obtainiumExport": "Obtainium Adat Exportálás", "obtainiumExport": "Obtainium Adat Exportálás",
"invalidInput": "Hibás bemenet", "invalidInput": "Hibás bemenet",
"importedX": "Importálva innen {}", "importedX": "Importálva innen {}",
"obtainiumImport": "Obtainium Adat Importálás", "obtainiumImport": "Obtainium Adat Importálás",
"importFromURLList": "Importálás URL listából", "importFromURLList": "Importálás URL listából",
"searchQuery": "Keresési lekérdezés", "searchQuery": "Keresési lekérdezés",
"appURLList": "App URL lista", "appURLList": "App URL lista",
"line": "Sor", "line": "Sor",
"searchX": "Keresés {}", "searchX": "Keresés {}",
"noResults": "Nincs találat", "noResults": "Nincs találat",
"importX": "Import {}", "importX": "Import {}",
"importedAppsIdDisclaimer": "Előfordulhat, hogy az importált appok helytelenül \"Nincs telepítve\" jelzéssel jelennek meg.\nA probléma megoldásához telepítse újra őket az Obtainiumon keresztül.\nEz nem érinti az alkalmazásadatokat.\n\nCsak az URL-ekre és a harmadik féltől származó importálási módszerekre vonatkozik..", "importedAppsIdDisclaimer": "Előfordulhat, hogy az importált appok helytelenül \"Nincs telepítve\" jelzéssel jelennek meg.\nA probléma megoldásához telepítse újra őket az Obtainiumon keresztül.\nEz nem érinti az alkalmazásadatokat.\n\nCsak az URL-ekre és a harmadik féltől származó importálási módszerekre vonatkozik..",
"importErrors": "Importálási hibák", "importErrors": "Importálási hibák",
"importedXOfYApps": "{}/{} app importálva.", "importedXOfYApps": "{}/{} app importálva.",
"followingURLsHadErrors": "A következő URL-ek hibákat tartalmaztak:", "followingURLsHadErrors": "A következő URL-ek hibákat tartalmaztak:",
"okay": "Oké", "okay": "Oké",
"selectURL": "Válassza ki az URL-t", "selectURL": "Válassza ki az URL-t",
"selectURLs": "Kiválasztott URL-ek", "selectURLs": "Kiválasztott URL-ek",
"pick": "Válasszon", "pick": "Válasszon",
"theme": "Téma", "theme": "Téma",
"dark": "Sötét", "dark": "Sötét",
"light": "Világos", "light": "Világos",
"followSystem": "Rendszer szerint", "followSystem": "Rendszer szerint",
"obtainium": "Obtainium", "obtainium": "Obtainium",
"materialYou": "Material You", "materialYou": "Material You",
"useBlackTheme": "Használjon tiszta fekete sötét témát", "useBlackTheme": "Használjon tiszta fekete sötét témát",
"appSortBy": "App rendezés...", "appSortBy": "App rendezés...",
"authorName": "Szerző/Név", "authorName": "Szerző/Név",
"nameAuthor": "Név/Szerző", "nameAuthor": "Név/Szerző",
"asAdded": "Mint Hozzáadott", "asAdded": "Mint Hozzáadott",
"appSortOrder": "Appok rendezése", "appSortOrder": "Appok rendezése",
"ascending": "Emelkedő", "ascending": "Emelkedő",
"descending": "Csökkenő", "descending": "Csökkenő",
"bgUpdateCheckInterval": "Háttérfrissítés ellenőrzés időköze", "bgUpdateCheckInterval": "Háttérfrissítés ellenőrzés időköze",
"neverManualOnly": "Soha csak manuális", "neverManualOnly": "Soha csak manuális",
"appearance": "Megjelenés", "appearance": "Megjelenés",
"showWebInAppView": "Forrás megjelenítése az Appok nézetben", "showWebInAppView": "Forrás megjelenítése az Appok nézetben",
"pinUpdates": "Frissítések kitűzése az App nézet tetejére", "pinUpdates": "Frissítések kitűzése az App nézet tetejére",
"updates": "Frissítések", "updates": "Frissítések",
"sourceSpecific": "Forrás-specifikus", "sourceSpecific": "Forrás-specifikus",
"appSource": "App forrás", "appSource": "App forrás",
"noLogs": "Nincsenek naplók", "noLogs": "Nincsenek naplók",
"appLogs": "App naplók", "appLogs": "App naplók",
"close": "Bezárás", "close": "Bezárás",
"share": "Megosztás", "share": "Megosztás",
"appNotFound": "App nem található", "appNotFound": "App nem található",
"obtainiumExportHyphenatedLowercase": "obtainium-export", "obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "Válasszon egy APK-t", "pickAnAPK": "Válasszon egy APK-t",
"appHasMoreThanOnePackage": "A(z) {} egynél több csomaggal rendelkezik:", "appHasMoreThanOnePackage": "A(z) {} egynél több csomaggal rendelkezik:",
"deviceSupportsXArch": "Eszköze támogatja a {} CPU architektúrát.", "deviceSupportsXArch": "Eszköze támogatja a {} CPU architektúrát.",
"deviceSupportsFollowingArchs": "Az eszköze a következő CPU architektúrákat támogatja:", "deviceSupportsFollowingArchs": "Az eszköze a következő CPU architektúrákat támogatja:",
"warning": "Figyelem", "warning": "Figyelem",
"sourceIsXButPackageFromYPrompt": "Az alkalmazás forrása „{}”, de a kiadási csomag innen származik: „{}”. Folytatja?", "sourceIsXButPackageFromYPrompt": "Az alkalmazás forrása „{}”, de a kiadási csomag innen származik: „{}”. Folytatja?",
"updatesAvailable": "Frissítések érhetők el", "updatesAvailable": "Frissítések érhetők el",
"updatesAvailableNotifDescription": "Értesíti a felhasználót, hogy frissítések állnak rendelkezésre egy vagy több, az Obtainium által nyomon követett alkalmazáshoz", "updatesAvailableNotifDescription": "Értesíti a felhasználót, hogy frissítések állnak rendelkezésre egy vagy több, az Obtainium által nyomon követett alkalmazáshoz",
"noNewUpdates": "Nincsenek új frissítések.", "noNewUpdates": "Nincsenek új frissítések.",
"xHasAnUpdate": "A(z) {} frissítést kapott.", "xHasAnUpdate": "A(z) {} frissítést kapott.",
"appsUpdated": "Alkalmazások frissítve", "appsUpdated": "Alkalmazások frissítve",
"appsUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy/több app frissítése megtörtént a háttérben", "appsUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy/több app frissítése megtörtént a háttérben",
"xWasUpdatedToY": "{} frissítve a következőre: {}.", "xWasUpdatedToY": "{} frissítve a következőre: {}.",
"errorCheckingUpdates": "Hiba a frissítések keresésekor", "errorCheckingUpdates": "Hiba a frissítések keresésekor",
"errorCheckingUpdatesNotifDescription": "Értesítés, amely akkor jelenik meg, ha a háttérbeli frissítések ellenőrzése sikertelen", "errorCheckingUpdatesNotifDescription": "Értesítés, amely akkor jelenik meg, ha a háttérbeli frissítések ellenőrzése sikertelen",
"appsRemoved": "Alkalmazások eltávolítva", "appsRemoved": "Alkalmazások eltávolítva",
"appsRemovedNotifDescription": "Értesíti a felhasználót egy vagy több alkalmazás eltávolításáról a betöltésük során fellépő hibák miatt", "appsRemovedNotifDescription": "Értesíti a felhasználót egy vagy több alkalmazás eltávolításáról a betöltésük során fellépő hibák miatt",
"xWasRemovedDueToErrorY": "A(z) {} a következő hiba miatt lett eltávolítva: {}", "xWasRemovedDueToErrorY": "A(z) {} a következő hiba miatt lett eltávolítva: {}",
"completeAppInstallation": "Teljes app telepítés", "completeAppInstallation": "Teljes app telepítés",
"obtainiumMustBeOpenToInstallApps": "Az Obtainiumnak megnyitva kell lennie az alkalmazások telepítéséhez", "obtainiumMustBeOpenToInstallApps": "Az Obtainiumnak megnyitva kell lennie az alkalmazások telepítéséhez",
"completeAppInstallationNotifDescription": "Megkéri a felhasználót, hogy térjen vissza az Obtainiumhoz, hogy befejezze az alkalmazás telepítését", "completeAppInstallationNotifDescription": "Megkéri a felhasználót, hogy térjen vissza az Obtainiumhoz, hogy befejezze az alkalmazás telepítését",
"checkingForUpdates": "Frissítések keresése", "checkingForUpdates": "Frissítések keresése",
"checkingForUpdatesNotifDescription": "Átmeneti értesítés, amely a frissítések keresésekor jelenik meg", "checkingForUpdatesNotifDescription": "Átmeneti értesítés, amely a frissítések keresésekor jelenik meg",
"pleaseAllowInstallPerm": "Kérjük, engedélyezze az Obtainiumnak az alkalmazások telepítését", "pleaseAllowInstallPerm": "Kérjük, engedélyezze az Obtainiumnak az alkalmazások telepítését",
"trackOnly": "Csak követés", "trackOnly": "Csak követés",
"errorWithHttpStatusCode": "Hiba {}", "errorWithHttpStatusCode": "Hiba {}",
"versionCorrectionDisabled": "Verzió korrekció letiltva (úgy tűnik, a beépülő modul nem működik)", "versionCorrectionDisabled": "Verzió korrekció letiltva (úgy tűnik, a beépülő modul nem működik)",
"unknown": "Ismeretlen", "unknown": "Ismeretlen",
"none": "Egyik sem", "none": "Egyik sem",
"never": "Soha", "never": "Soha",
"latestVersionX": "Legújabb verzió: {}", "latestVersionX": "Legújabb verzió: {}",
"installedVersionX": "Telepített verzió: {}", "installedVersionX": "Telepített verzió: {}",
"lastUpdateCheckX": "Frissítés ellenőrizve: {}", "lastUpdateCheckX": "Frissítés ellenőrizve: {}",
"remove": "Eltávolítás", "remove": "Eltávolítás",
"yesMarkUpdated": "Igen, megjelölés frissítettként", "yesMarkUpdated": "Igen, megjelölés frissítettként",
"fdroid": "F-Droid Official", "fdroid": "F-Droid Official",
"appIdOrName": "App ID vagy név", "appIdOrName": "App ID vagy név",
"appId": "App ID", "appId": "App ID",
"appWithIdOrNameNotFound": "Nem található app ezzel az azonosítóval vagy névvel", "appWithIdOrNameNotFound": "Nem található app ezzel az azonosítóval vagy névvel",
"reposHaveMultipleApps": "A repók több alkalmazást is tartalmazhatnak", "reposHaveMultipleApps": "A repók több alkalmazást is tartalmazhatnak",
"fdroidThirdPartyRepo": "F-Droid Harmadik-fél Repo", "fdroidThirdPartyRepo": "F-Droid Harmadik-fél Repo",
"steam": "Steam", "steam": "Steam",
"steamMobile": "Steam Mobile", "steamMobile": "Steam Mobile",
"steamChat": "Steam Chat", "steamChat": "Steam Chat",
"install": "Telepít", "install": "Telepít",
"markInstalled": "Telepítettnek jelöl", "markInstalled": "Telepítettnek jelöl",
"update": "Frissít", "update": "Frissít",
"markUpdated": "Frissítettnek jelöl", "markUpdated": "Frissítettnek jelöl",
"additionalOptions": "További lehetőségek", "additionalOptions": "További lehetőségek",
"disableVersionDetection": "Verzió érzékelés letiltása", "disableVersionDetection": "Verzió érzékelés letiltása",
"noVersionDetectionExplanation": "Ezt a beállítást csak olyan alkalmazásoknál szabad használni, ahol a verzióérzékelés nem működik megfelelően.", "noVersionDetectionExplanation": "Ezt a beállítást csak olyan alkalmazásoknál szabad használni, ahol a verzióérzékelés nem működik megfelelően.",
"downloadingX": "{} letöltés", "downloadingX": "{} letöltés",
"downloadNotifDescription": "Értesíti a felhasználót az app letöltésének előrehaladásáról", "downloadNotifDescription": "Értesíti a felhasználót az app letöltésének előrehaladásáról",
"noAPKFound": "Nem található APK", "noAPKFound": "Nem található APK",
"noVersionDetection": "Nincs verzió érzékelés", "noVersionDetection": "Nincs verzió érzékelés",
"categorize": "Kategorizálás", "categorize": "Kategorizálás",
"categories": "Kategóriák", "categories": "Kategóriák",
"category": "Kategória", "category": "Kategória",
"noCategory": "Nincs kategória", "noCategory": "Nincs kategória",
"deleteCategoryQuestion": "Törli a kategóriát?", "noCategories": "No Categories",
"categoryDeleteWarning": "A(z) {} összes app kategorizálatlan állapotba kerül.", "deleteCategoryQuestion": "Törli a kategóriát?",
"addCategory": "Új kategória", "categoryDeleteWarning": "A(z) {} összes app kategorizálatlan állapotba kerül.",
"label": "Címke", "addCategory": "Új kategória",
"language": "Nyelv", "label": "Címke",
"copiedToClipboard": "Másolva a vágólapra", "language": "Nyelv",
"storagePermissionDenied": "Tárhely engedély megtagadva", "copiedToClipboard": "Másolva a vágólapra",
"selectedCategorizeWarning": "Ez felváltja a kiválasztott alkalmazások meglévő kategória-beállításait.", "storagePermissionDenied": "Tárhely engedély megtagadva",
"filterAPKsByRegEx": "Az APK-k szűrése reguláris kifejezéssel", "selectedCategorizeWarning": "Ez felváltja a kiválasztott alkalmazások meglévő kategória-beállításait.",
"removeFromObtainium": "Eltávolítás az Obtainiumból", "filterAPKsByRegEx": "Az APK-k szűrése reguláris kifejezéssel",
"uninstallFromDevice": "Eltávolítás a készülékről", "removeFromObtainium": "Eltávolítás az Obtainiumból",
"onlyWorksWithNonVersionDetectApps": "Csak azoknál az alkalmazásoknál működik, amelyeknél a verzióérzékelés le van tiltva.", "uninstallFromDevice": "Eltávolítás a készülékről",
"releaseDateAsVersion": "Használja a Kiadás dátumát, mint verziót", "onlyWorksWithNonVersionDetectApps": "Csak azoknál az alkalmazásoknál működik, amelyeknél a verzióérzékelés le van tiltva.",
"releaseDateAsVersionExplanation": "Ezt a beállítást csak olyan alkalmazásoknál szabad használni, ahol a verzió érzékelése nem működik megfelelően, de elérhető a kiadás dátuma.", "releaseDateAsVersion": "Használja a Kiadás dátumát, mint verziót",
"changes": "Változtatások", "releaseDateAsVersionExplanation": "Ezt a beállítást csak olyan alkalmazásoknál szabad használni, ahol a verzió érzékelése nem működik megfelelően, de elérhető a kiadás dátuma.",
"releaseDate": "Kiadás dátuma", "changes": "Változtatások",
"importFromURLsInFile": "Importálás fájlban található URL-ből (mint pl. OPML)", "releaseDate": "Kiadás dátuma",
"versionDetection": "Verzió érzékelés", "importFromURLsInFile": "Importálás fájlban található URL-ből (mint pl. OPML)",
"standardVersionDetection": "Alapért. verzió érzékelés", "versionDetection": "Verzió érzékelés",
"groupByCategory": "Csoportosítás Kategória alapján", "standardVersionDetection": "Alapért. verzió érzékelés",
"autoApkFilterByArch": "Ha lehetséges, próbálja CPU architektúra szerint szűrni az APK-okat", "groupByCategory": "Csoportosítás Kategória alapján",
"overrideSource": "Forrás felülbírálása", "autoApkFilterByArch": "Ha lehetséges, próbálja CPU architektúra szerint szűrni az APK-okat",
"dontShowAgain": "Ne mutassa ezt újra", "overrideSource": "Forrás felülbírálása",
"dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést", "dontShowAgain": "Ne mutassa ezt újra",
"dontShowAPKOriginWarnings": "Ne jelenítsen meg az APK eredetére vonatkozó figyelmeztetéseket", "dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést",
"moveNonInstalledAppsToBottom": "Helyezze át a nem telepített appokat az App nézet aljára", "dontShowAPKOriginWarnings": "Ne jelenítsen meg az APK eredetére vonatkozó figyelmeztetéseket",
"gitlabPATLabel": "GitLab Personal Access Token\n(Engedélyezi a Keresést és jobb APK felfedezés)", "moveNonInstalledAppsToBottom": "Helyezze át a nem telepített appokat az App nézet aljára",
"about": "Rólunk", "gitlabPATLabel": "GitLab Personal Access Token\n(Engedélyezi a Keresést és jobb APK felfedezés)",
"requiresCredentialsInSettings": "Ehhez további hitelesítő adatokra van szükség (a Beállításokban)", "about": "Rólunk",
"checkOnStart": "Egyszer az alkalmazás indításakor is", "requiresCredentialsInSettings": "Ehhez további hitelesítő adatokra van szükség (a Beállításokban)",
"tryInferAppIdFromCode": "Próbálja kikövetkeztetni az app azonosítót a forráskódból", "checkOnStart": "Egyszer az alkalmazás indításakor is",
"removeOnExternalUninstall": "A külsőleg eltávolított appok auto. eltávolítása", "tryInferAppIdFromCode": "Próbálja kikövetkeztetni az app azonosítót a forráskódból",
"pickHighestVersionCode": "A legmagasabb verziószámú APK auto. kiválasztása", "removeOnExternalUninstall": "A külsőleg eltávolított appok auto. eltávolítása",
"checkUpdateOnDetailPage": "Frissítések keresése az app részleteit tartalmazó oldal megnyitásakor", "pickHighestVersionCode": "A legmagasabb verziószámú APK auto. kiválasztása",
"disablePageTransitions": "Lap áttűnési animációk letiltása", "checkUpdateOnDetailPage": "Frissítések keresése az app részleteit tartalmazó oldal megnyitásakor",
"reversePageTransitions": "Fordított lap áttűnési animációk", "disablePageTransitions": "Lap áttűnési animációk letiltása",
"minStarCount": "Minimális csillag szám", "reversePageTransitions": "Fordított lap áttűnési animációk",
"addInfoBelow": "Adja hozzá ezt az infót alább.", "minStarCount": "Minimális csillag szám",
"addInfoInSettings": "Adja hozzá ezt az infót a Beállításokban.", "addInfoBelow": "Adja hozzá ezt az infót alább.",
"githubSourceNote": "A GitHub sebességkorlátos elkerülhető API-kulcs használatával.", "addInfoInSettings": "Adja hozzá ezt az infót a Beállításokban.",
"gitlabSourceNote": "Előfordulhat, hogy a GitLab APK kibontása nem működik API-kulcs nélkül.", "githubSourceNote": "A GitHub sebességkorlátozás elkerülhető API-kulcs használatával.",
"sortByFileNamesNotLinks": "Fájlnevek szerinti elrendezés teljes linkek helyett", "gitlabSourceNote": "Előfordulhat, hogy a GitLab APK kibontása nem működik API-kulcs nélkül.",
"filterReleaseNotesByRegEx": "Kiadási megjegyzések szűrése reguláris kifejezéssel", "sortByFileNamesNotLinks": "Fájlnevek szerinti elrendezés teljes linkek helyett",
"customLinkFilterRegex": "Egyéni APK hivatkozásszűrő reguláris kifejezéssel (Alapérték '.apk$')", "filterReleaseNotesByRegEx": "Kiadási megjegyzések szűrése reguláris kifejezéssel",
"appsPossiblyUpdated": "App frissítési kísérlet", "customLinkFilterRegex": "Egyéni APK hivatkozásszűrő reguláris kifejezéssel (Alapérték '.apk$')",
"appsPossiblyUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy vagy több alkalmazás frissítése lehetséges a háttérben", "appsPossiblyUpdated": "App frissítési kísérlet",
"xWasPossiblyUpdatedToY": "{} frissítve lehet erre {}.", "appsPossiblyUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy vagy több alkalmazás frissítése lehetséges a háttérben",
"backgroundUpdateReqsExplanation": "Előfordulhat, hogy nem minden appnál lehetséges a háttérbeli frissítés.", "xWasPossiblyUpdatedToY": "{} frissítve lehet erre {}.",
"backgroundUpdateLimitsExplanation": "A háttérben történő telepítés sikeressége csak az Obtainium megnyitásakor állapítható meg.", "backgroundUpdateReqsExplanation": "Előfordulhat, hogy nem minden appnál lehetséges a háttérbeli frissítés.",
"verifyLatestTag": "Ellenőrizze a „legújabb” címkét", "backgroundUpdateLimitsExplanation": "A háttérben történő telepítés sikeressége csak az Obtainium megnyitásakor állapítható meg.",
"exemptFromBackgroundUpdates": "Mentes a háttérben történő frissítések alól (ha engedélyezett)", "verifyLatestTag": "Ellenőrizze a „legújabb” címkét",
"bgUpdatesOnWiFiOnly": "Tiltsa le a háttérben frissítéseket, ha nincs Wi-Fi-n", "intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
"autoSelectHighestVersionCode": "A legmagasabb verziószámú APK auto. kiválasztása", "intermediateLinkNotFound": "Intermediate link not found",
"versionExtractionRegEx": "Verzió kibontása reguláris kifejezéssel", "exemptFromBackgroundUpdates": "Mentes a háttérben történő frissítések alól (ha engedélyezett)",
"matchGroupToUse": "Párosítsa a csoportot a használathoz", "bgUpdatesOnWiFiOnly": "Tiltsa le a háttérben frissítéseket, ha nincs Wi-Fi-n",
"highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat", "autoSelectHighestVersionCode": "A legmagasabb verziószámú APK auto. kiválasztása",
"pickExportDir": "Válassza az Exportálási könyvtárat", "versionExtractionRegEx": "Verzió kibontása reguláris kifejezéssel",
"autoExportOnChanges": "Auto-exportálás a változások után", "matchGroupToUse": "Párosítsa a csoportot a használathoz",
"filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel", "highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat",
"trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t", "pickExportDir": "Válassza az Exportálási könyvtárat",
"removeAppQuestion": { "autoExportOnChanges": "Auto-exportálás a változások után",
"one": "Eltávolítja az alkalmazást?", "filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel",
"other": "Eltávolítja az alkalmazást?" "trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t",
}, "dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése",
"tooManyRequestsTryAgainInMinutes": { "reverseSort": "Fordított rendezés",
"one": "Túl sok kérés (korlátozott arány) próbálja újra {} perc múlva", "debugMenu": "Hibakereső menü",
"other": "Túl sok kérés (korlátozott arány) próbálja újra {} perc múlva" "bgTaskStarted": "A háttérfeladat elindult ellenőrizze a naplókat.",
}, "enableBackgroundUpdates": "Frissítések a háttérben",
"bgUpdateGotErrorRetryInMinutes": { "runBgCheckNow": "Futtassa a Háttérben frissítés ellenőrzését most",
"one": "A háttérfrissítések ellenőrzése {}-t észlelt, {} perc múlva ütemezi az újrapróbálkozást", "versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"other": "A háttérfrissítések ellenőrzése {}-t észlelt, {} perc múlva ütemezi az újrapróbálkozást" "installing": "Installing",
}, "skipUpdateNotifications": "Skip update notifications",
"bgCheckFoundUpdatesWillNotifyIfNeeded": { "updatesAvailableNotifChannel": "Frissítések érhetők el",
"one": "A háttérfrissítés ellenőrzése {} frissítést talált szükség esetén értesíti a felhasználót", "appsUpdatedNotifChannel": "Alkalmazások frissítve",
"other": "A háttérfrissítés ellenőrzése {} frissítést talált szükség esetén értesíti a felhasználót" "appsPossiblyUpdatedNotifChannel": "App frissítési kísérlet",
}, "errorCheckingUpdatesNotifChannel": "Hiba a frissítések keresésekor",
"apps": { "appsRemovedNotifChannel": "Alkalmazások eltávolítva",
"one": "{} app", "downloadingXNotifChannel": "{} letöltés",
"other": "{} app" "completeAppInstallationNotifChannel": "Teljes app telepítés",
}, "checkingForUpdatesNotifChannel": "Frissítések keresése",
"url": { "removeAppQuestion": {
"one": "{} URL", "one": "Eltávolítja az alkalmazást?",
"other": "{} URL" "other": "Eltávolítja az alkalmazást?"
}, },
"minute": { "tooManyRequestsTryAgainInMinutes": {
"one": "{} perc", "one": "Túl sok kérés (korlátozott arány) próbálja újra {} perc múlva",
"other": "{} perc" "other": "Túl sok kérés (korlátozott arány) próbálja újra {} perc múlva"
}, },
"hour": { "bgUpdateGotErrorRetryInMinutes": {
"one": "{} óra", "one": "A háttérfrissítések ellenőrzése {}-t észlelt, {} perc múlva ütemezi az újrapróbálkozást",
"other": "{} óra" "other": "A háttérfrissítések ellenőrzése {}-t észlelt, {} perc múlva ütemezi az újrapróbálkozást"
}, },
"day": { "bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "{} nap", "one": "A háttérfrissítés ellenőrzése {} frissítést talált szükség esetén értesíti a felhasználót",
"other": "{} nap" "other": "A háttérfrissítés ellenőrzése {} frissítést talált szükség esetén értesíti a felhasználót"
}, },
"clearedNLogsBeforeXAfterY": { "apps": {
"one": "{n} napló törölve (előtte = {előtte}, utána = {utána})", "one": "{} app",
"other": "{n} napló törölve (előtte = {előtte}, utána = {utána})" "other": "{} app"
}, },
"xAndNMoreUpdatesAvailable": { "url": {
"one": "A(z) {} és 1 további alkalmazás frissítéseket kapott.", "one": "{} URL",
"other": "{} és {} további alkalmazás frissítéseket kapott." "other": "{} URL"
}, },
"xAndNMoreUpdatesInstalled": { "minute": {
"one": "A(z) {} és 1 további alkalmazás frissítve.", "one": "{} perc",
"other": "{} és {} további alkalmazás frissítve." "other": "{} perc"
}, },
"xAndNMoreUpdatesPossiblyInstalled": { "hour": {
"one": "{} és 1 további alkalmazás is frissült.", "one": "{} óra",
"other": "{} és {} további alkalmazás is frissült." "other": "{} óra"
} },
} "day": {
"one": "{} nap",
"other": "{} nap"
},
"clearedNLogsBeforeXAfterY": {
"one": "{n} napló törölve (előtte = {előtte}, utána = {utána})",
"other": "{n} napló törölve (előtte = {előtte}, utána = {utána})"
},
"xAndNMoreUpdatesAvailable": {
"one": "A(z) {} és 1 további alkalmazás frissítéseket kapott.",
"other": "{} és {} további alkalmazás frissítéseket kapott."
},
"xAndNMoreUpdatesInstalled": {
"one": "A(z) {} és 1 további alkalmazás frissítve.",
"other": "{} és {} további alkalmazás frissítve."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} és 1 további alkalmazás is frissült.",
"other": "{} és {} további alkalmazás is frissült."
}
}

View File

@ -242,9 +242,12 @@
"appsPossiblyUpdated": "App Updates Attempted", "appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background", "appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.", "xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"enableBackgroundUpdates": "Enable background updates",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.", "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.", "backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag", "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)", "exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi", "bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK", "autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
@ -255,6 +258,22 @@
"autoExportOnChanges": "Auto-export on changes", "autoExportOnChanges": "Auto-export on changes",
"filterVersionsByRegEx": "Filter Versions by Regular Expression", "filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK", "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"updatesAvailableNotifChannel": "Aggiornamenti disponibili",
"appsUpdatedNotifChannel": "App aggiornate",
"appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
"errorCheckingUpdatesNotifChannel": "Controllo degli errori per gli aggiornamenti",
"appsRemovedNotifChannel": "App rimosse",
"downloadingXNotifChannel": "Scaricamento di {} in corso",
"completeAppInstallationNotifChannel": "Completa l'installazione dell'app",
"checkingForUpdatesNotifChannel": "Controllo degli aggiornamenti in corso",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Rimuovere l'app?", "one": "Rimuovere l'app?",
"other": "Rimuovere le app?" "other": "Rimuovere le app?"
@ -307,4 +326,4 @@
"one": "{} and 1 more app may have been updated.", "one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated." "other": "{} and {} more apps may have been updated."
} }
} }

View File

@ -258,6 +258,22 @@
"autoExportOnChanges": "変更があった際に自動でエクスポートする", "autoExportOnChanges": "変更があった際に自動でエクスポートする",
"filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする", "filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする",
"trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する", "trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Reverse sorting",
"debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"updatesAvailableNotifChannel": "アップデートが利用可能",
"appsUpdatedNotifChannel": "アプリをアップデートしました",
"appsPossiblyUpdatedNotifChannel": "アプリのアップデートを試行",
"errorCheckingUpdatesNotifChannel": "アップデート確認中のエラー",
"appsRemovedNotifChannel": "削除されたアプリ",
"downloadingXNotifChannel": "{} をダウンロード中",
"completeAppInstallationNotifChannel": "アプリのインストールを完了する",
"checkingForUpdatesNotifChannel": "アップデートを確認中",
"removeAppQuestion": { "removeAppQuestion": {
"one": "アプリを削除しますか?", "one": "アプリを削除しますか?",
"other": "アプリを削除しますか?" "other": "アプリを削除しますか?"
@ -310,4 +326,4 @@
"one": "{} とさらに 1 個のアプリがアップデートされた可能性があります。", "one": "{} とさらに 1 個のアプリがアップデートされた可能性があります。",
"other": "{} とさらに {} 個のアプリがアップデートされた可能性があります。" "other": "{} とさらに {} 個のアプリがアップデートされた可能性があります。"
} }
} }

View File

@ -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 {}", "invalidURLForSource": "Nieprawidłowy adres URL aplikacji {}",
"noReleaseFound": "Nie można znaleźć odpowiedniego wydania", "noReleaseFound": "Nie można znaleźć odpowiedniego wydania",
"noVersionFound": "Nie można określić wersji wydania", "noVersionFound": "Nie można określić wersji wydania",
@ -26,6 +16,7 @@
"fallbackToOlderReleases": "Powracaj do starszych wersji", "fallbackToOlderReleases": "Powracaj do starszych wersji",
"filterReleaseTitlesByRegEx": "Filtruj tytuły wydań wg. wyrażeń regularnych", "filterReleaseTitlesByRegEx": "Filtruj tytuły wydań wg. wyrażeń regularnych",
"invalidRegEx": "Nieprawidłowe wyrażenie regularne", "invalidRegEx": "Nieprawidłowe wyrażenie regularne",
"noDescription": "Brak opisu",
"cancel": "Anuluj", "cancel": "Anuluj",
"continue": "Kontynuuj", "continue": "Kontynuuj",
"requiredInBrackets": "(Wymagane)", "requiredInBrackets": "(Wymagane)",
@ -34,10 +25,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 +43,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,11 +61,12 @@
"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",
"markXSelectedAppsAsUpdated": "Oznaczyć {} wybranych aplikacji jako zaktualizowane?", "markXSelectedAppsAsUpdated": "Oznaczyć {} wybranych aplikacji jako zaktualizowane?",
"no": "Nie",
"yes": "Tak", "yes": "Tak",
"markSelectedAppsUpdated": "Oznacz wybrane aplikacje jako zaktualizowane", "markSelectedAppsUpdated": "Oznacz wybrane aplikacje jako zaktualizowane",
"pinToTop": "Przypnij", "pinToTop": "Przypnij",
@ -111,6 +103,7 @@
"importErrors": "Błędy importowania", "importErrors": "Błędy importowania",
"importedXOfYApps": "Zaimportowano {} z {} aplikacji.", "importedXOfYApps": "Zaimportowano {} z {} aplikacji.",
"followingURLsHadErrors": "Następujące adresy URL zawierały błędy:", "followingURLsHadErrors": "Następujące adresy URL zawierały błędy:",
"okay": "Okej",
"selectURL": "Wybierz adres URL", "selectURL": "Wybierz adres URL",
"selectURLs": "Wybierz adresy URL", "selectURLs": "Wybierz adresy URL",
"pick": "Wybierz", "pick": "Wybierz",
@ -149,20 +142,20 @@
"warning": "Uwaga", "warning": "Uwaga",
"sourceIsXButPackageFromYPrompt": "Źródłem aplikacji jest '{}', ale pakiet wydania pochodzi z '{}'. Kontynuować?", "sourceIsXButPackageFromYPrompt": "Źródłem aplikacji jest '{}', ale pakiet wydania pochodzi z '{}'. Kontynuować?",
"updatesAvailable": "Dostępne aktualizacje", "updatesAvailable": "Dostępne aktualizacje",
"updatesAvailableNotifDescription": "Powiadamia użytkownika o dostępności aktualizacji dla jednej lub więcej aplikacji obserwowanych przez Obtainium", "updatesAvailableNotifDescription": "Informuje o dostępności aktualizacji dla jednej lub więcej aplikacji obserwowanych przez Obtainium",
"noNewUpdates": "Brak nowych aktualizacji.", "noNewUpdates": "Brak nowych aktualizacji.",
"xHasAnUpdate": "{} ma aktualizację.", "xHasAnUpdate": "{} ma aktualizację.",
"appsUpdated": "Zaktualizowane aplikacje", "appsUpdated": "Zaktualizowano aplikacje",
"appsUpdatedNotifDescription": "Powiadamia użytkownika, gdy jedna lub więcej aplikacji zostało zaktualizowanych w tle", "appsUpdatedNotifDescription": "Informuje, gdy co najmniej jedna aplikacja została zaktualizowana w tle",
"xWasUpdatedToY": "{} zaktualizowano do {}.", "xWasUpdatedToY": "{} zaktualizowano do {}.",
"errorCheckingUpdates": "Sprawdzanie błędów aktualizacji", "errorCheckingUpdates": "Błąd sprawdzania aktualizacji",
"errorCheckingUpdatesNotifDescription": "Powiadomienie wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się", "errorCheckingUpdatesNotifDescription": "Jest wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się",
"appsRemoved": "Usunięte aplikacje", "appsRemoved": "Usunięte aplikacje",
"appsRemovedNotifDescription": "Powiadamia użytkownika, gdy jedna lub więcej aplikacji zostało usuniętych z powodu błędów wczytywania", "appsRemovedNotifDescription": "Informuje, gdy co najmniej jedna aplikacja została usunięta z powodu błędów podczas wczytywania",
"xWasRemovedDueToErrorY": "Usunięto {} z powodu błędu: {}", "xWasRemovedDueToErrorY": "Usunięto {} z powodu błędu: {}",
"completeAppInstallation": "Ukończenie instalacji aplikacji", "completeAppInstallation": "Ukończenie instalacji aplikacji",
"obtainiumMustBeOpenToInstallApps": "Aby zainstalować aplikacje, Obtainium musi być otwarte", "obtainiumMustBeOpenToInstallApps": "Aby zainstalować aplikacje, Obtainium musi być otwarte",
"completeAppInstallationNotifDescription": "Prosi użytkownika o powrót do Obtainium w celu dokończenia instalacji aplikacji", "completeAppInstallationNotifDescription": "Informuje o możliwości powrotu do Obtainium w celu dokończenia instalacji aplikacji",
"checkingForUpdates": "Sprawdzanie aktualizacji", "checkingForUpdates": "Sprawdzanie aktualizacji",
"checkingForUpdatesNotifDescription": "Tymczasowe powiadomienie pojawiające się podczas sprawdzania aktualizacji", "checkingForUpdatesNotifDescription": "Tymczasowe powiadomienie pojawiające się podczas sprawdzania aktualizacji",
"pleaseAllowInstallPerm": "Pozwól Obtainium instalować aplikacje", "pleaseAllowInstallPerm": "Pozwól Obtainium instalować aplikacje",
@ -179,6 +172,7 @@
"yesMarkUpdated": "Tak, oznacz jako zaktualizowane", "yesMarkUpdated": "Tak, oznacz jako zaktualizowane",
"fdroid": "Oficjalny F-Droid", "fdroid": "Oficjalny F-Droid",
"appIdOrName": "ID aplikacji lub nazwa", "appIdOrName": "ID aplikacji lub nazwa",
"appId": "ID aplikacji",
"appWithIdOrNameNotFound": "Nie znaleziono aplikacji o tym identyfikatorze lub nazwie", "appWithIdOrNameNotFound": "Nie znaleziono aplikacji o tym identyfikatorze lub nazwie",
"reposHaveMultipleApps": "Repozytoria mogą zawierać wiele aplikacji", "reposHaveMultipleApps": "Repozytoria mogą zawierać wiele aplikacji",
"fdroidThirdPartyRepo": "Zewnętrzne repo F-Droid", "fdroidThirdPartyRepo": "Zewnętrzne repo F-Droid",
@ -193,7 +187,7 @@
"disableVersionDetection": "Wyłącz wykrywanie wersji", "disableVersionDetection": "Wyłącz wykrywanie wersji",
"noVersionDetectionExplanation": "Opcja ta powinna być używana tylko w przypadku aplikacji, w których wykrywanie wersji nie działa poprawnie.", "noVersionDetectionExplanation": "Opcja ta powinna być używana tylko w przypadku aplikacji, w których wykrywanie wersji nie działa poprawnie.",
"downloadingX": "Pobieranie {}", "downloadingX": "Pobieranie {}",
"downloadNotifDescription": "Powiadamia użytkownika o postępach w pobieraniu aplikacji", "downloadNotifDescription": "Informuje o postępach w pobieraniu aplikacji",
"noAPKFound": "Nie znaleziono pakietu APK", "noAPKFound": "Nie znaleziono pakietu APK",
"noVersionDetection": "Bez wykrywania wersji", "noVersionDetection": "Bez wykrywania wersji",
"categorize": "Kategoryzuj", "categorize": "Kategoryzuj",
@ -245,22 +239,41 @@
"sortByFileNamesNotLinks": "Sortuj wg nazw plików zamiast pełnych linków", "sortByFileNamesNotLinks": "Sortuj wg nazw plików zamiast pełnych linków",
"filterReleaseNotesByRegEx": "Filtruj informacje o wersji według wyrażenia regularnego", "filterReleaseNotesByRegEx": "Filtruj informacje o wersji według wyrażenia regularnego",
"customLinkFilterRegex": "Filtruj linki APK według wyrażenia regularnego (domyślnie \".apk$\")", "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", "appsPossiblyUpdatedNotifDescription": "Powiadamia, gdy co najmniej jedna aktualizacja aplikacji została potencjalnie zastosowana w tle",
"xWasPossiblyUpdatedToY": "{} być może zaktualizowano do {}.", "xWasPossiblyUpdatedToY": "{} być może zaktualizowano do {}.",
"enableBackgroundUpdates": "Włącz aktualizacje w tle",
"backgroundUpdateReqsExplanation": "Aktualizacje w tle mogą nie być możliwe dla wszystkich aplikacji.", "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.", "backgroundUpdateLimitsExplanation": "Powodzenie instalacji w tle można określić dopiero po otwarciu Obtainium.",
"verifyLatestTag": "Zweryfikuj najnowszy tag", "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)", "exemptFromBackgroundUpdates": "Wyklucz z uaktualnień w tle (jeśli są włączone)",
"bgUpdatesOnWiFiOnly": "Wyłącz aktualizacje w tle, gdy nie ma połączenia z Wi-Fi", "bgUpdatesOnWiFiOnly": "Wyłącz aktualizacje w tle, gdy nie ma połączenia z Wi-Fi",
"autoSelectHighestVersionCode": "Automatycznie wybierz najwyższy kod wersji APK", "autoSelectHighestVersionCode": "Automatycznie wybierz najwyższy kod wersji APK",
"versionExtractionRegEx": "Wyrażenie regularne wyodrębniające wersję", "versionExtractionRegEx": "Wyrażenie regularne wyodrębniające wersję",
"matchGroupToUse": "Dopasuj grupę do użycia", "matchGroupToUse": "Dopasuj grupę do użycia dla wyrażenia regularnego wyodrębniania wersji",
"highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe", "highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
"pickExportDir": "Wybierz katalog eksportu", "pickExportDir": "Wybierz katalog eksportu",
"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": "Utrzymaj kolejność wydań z interfejsu API",
"reverseSort": "Odwrotne sortowanie",
"debugMenu": "Menu debugowania",
"bgTaskStarted": "Uruchomiono zadanie w tle - sprawdź logi.",
"runBgCheckNow": "Wymuś sprawdzenie aktualizacji w tle",
"versionExtractWholePage": "Zastosuj wyrażenie regularne wyodrębniania wersji dla całej strony",
"installing": "Instalacja",
"skipUpdateNotifications": "Pomiń powiadomienia o aktualizacjach",
"updatesAvailableNotifChannel": "Dostępne aktualizacje aplikacji",
"appsUpdatedNotifChannel": "Zaktualizowane aplikacje",
"appsPossiblyUpdatedNotifChannel": "Informuj o próbach aktualizacji",
"errorCheckingUpdatesNotifChannel": "Błędy sprawdzania aktualizacji",
"appsRemovedNotifChannel": "Usunięte aplikacje",
"downloadingXNotifChannel": "Pobieranie aplikacji",
"completeAppInstallationNotifChannel": "Ukończenie instalacji aplikacji",
"checkingForUpdatesNotifChannel": "Sprawdzanie dostępności aktualizacji",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Usunąć aplikację?", "one": "Usunąć aplikację?",
"few": "Usunąć aplikacje?", "few": "Usunąć aplikacje?",
@ -273,6 +286,12 @@
"many": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} minut", "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": { "bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "W tle znaleziono {} aktualizację - w razie potrzeby użytkownik zostanie o tym powiadomiony", "one": "W tle znaleziono {} aktualizację - w razie potrzeby użytkownik zostanie o tym powiadomiony",
"few": "W tle znaleziono {} aktualizacje - w razie potrzeby użytkownik zostanie o tym powiadomiony", "few": "W tle znaleziono {} aktualizacje - w razie potrzeby użytkownik zostanie o tym powiadomiony",
@ -332,8 +351,5 @@
"few": "{} i {} inne apki mogły zostać zaktualizowane.", "few": "{} i {} inne apki mogły zostać zaktualizowane.",
"many": "{} i {} innych apek mogło zostać zaktualizowanych.", "many": "{} i {} innych apek mogło zostać zaktualizowanych.",
"other": "{} i {} inne apki mogły zostać zaktualizowane." "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"
} }

View File

@ -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,14 +250,30 @@
"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": "Reter a ordem de lançamento da API",
"reverseSort": "Ordenação reversa",
"debugMenu": "Menu Debug",
"bgTaskStarted": "Tarefa em segundo plano iniciada - verifique os logs.",
"runBgCheckNow": "Execute a verificação de atualização em segundo plano agora",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"updatesAvailableNotifChannel": "Atualizações Disponíveis",
"appsUpdatedNotifChannel": "Apps Atualizados",
"appsPossiblyUpdatedNotifChannel": "Tentativas de atualização de Apps",
"errorCheckingUpdatesNotifChannel": "Erro ao Procurar por Atualizações",
"appsRemovedNotifChannel": "Apps Removidos",
"downloadingXNotifChannel": "Baixando {}",
"completeAppInstallationNotifChannel": "Instalação completa do App",
"checkingForUpdatesNotifChannel": "Checando por Atualizações",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Remover App?", "one": "Remover App?",
"other": "Remover Apps?" "other": "Remover Apps?"
@ -310,4 +326,4 @@
"one": "{} e 1 outro app pode ter sido atualizado.", "one": "{} e 1 outro app pode ter sido atualizado.",
"other": "{} e {} outros apps podem ter sido atualizados." "other": "{} e {} outros apps podem ter sido atualizados."
} }
} }

View File

@ -236,25 +236,44 @@
"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": "Выбрать каталог для экспорта",
"autoExportOnChanges": "Автоэкспорт при изменениях",
"filterVersionsByRegEx": "Фильтровать версии по регулярному выражению",
"trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
"dontSortReleasesList": "Сохранить порядок выпусков от API",
"reverseSort": "Обратная сортировка",
"debugMenu": "Меню Отладки",
"bgTaskStarted": "Фоновая задача начата - проверьте журналы.",
"runBgCheckNow": "Запустить проверку фонового обновления сейчас",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"updatesAvailableNotifChannel": "Доступны обновления",
"appsUpdatedNotifChannel": "Приложения обновлены",
"appsPossiblyUpdatedNotifChannel": "Попытки обновления приложений",
"errorCheckingUpdatesNotifChannel": "Ошибка при проверке обновлений",
"appsRemovedNotifChannel": "Приложение удалено",
"downloadingXNotifChannel": "Загрузка {}",
"completeAppInstallationNotifChannel": "Завершение установки приложения",
"checkingForUpdatesNotifChannel": "Проверка обновлений",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Удалить приложение?", "one": "Удалить приложение?",
"other": "Удалить приложения?" "other": "Удалить приложения?"
@ -300,11 +319,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": "{} и ещё {} приложений могли быть обновлены."
} }
} }

View File

@ -51,9 +51,9 @@
"percentProgress": "进度:{}%", "percentProgress": "进度:{}%",
"pleaseWait": "请稍候", "pleaseWait": "请稍候",
"updateAvailable": "更新可用", "updateAvailable": "更新可用",
"estimateInBracketsShort": "(推测)", "estimateInBracketsShort": "推测",
"notInstalled": "未安装", "notInstalled": "未安装",
"estimateInBrackets": "(推测)", "estimateInBrackets": "推测",
"selectAll": "全选", "selectAll": "全选",
"deselectN": "取消选择 {}", "deselectN": "取消选择 {}",
"xWillBeRemovedButRemainInstalled": "{} 将从 Obtainium 中删除,但仍安装在您的设备中。", "xWillBeRemovedButRemainInstalled": "{} 将从 Obtainium 中删除,但仍安装在您的设备中。",
@ -238,24 +238,42 @@
"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”标签",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)", "intermediateLinkRegex": "筛选一个首先访问的“中转”链接(正则表达式)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi", "intermediateLinkNotFound": "未找到“中转”链接",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK", "exemptFromBackgroundUpdates": "单独禁用后台更新(若已经全局启用)",
"versionExtractionRegEx": "Version Extraction RegEx", "bgUpdatesOnWiFiOnly": "未连接 Wi-Fi 时禁用后台更新",
"matchGroupToUse": "Match Group to Use", "autoSelectHighestVersionCode": "自动选择版本号最高的 APK 文件",
"highlightTouchTargets": "Highlight less obvious touch targets", "versionExtractionRegEx": "获取版本号的正则表达式",
"pickExportDir": "Pick Export Directory", "matchGroupToUse": "引用的捕获组",
"autoExportOnChanges": "Auto-export on changes", "highlightTouchTargets": "突出展示不明显的触摸区域",
"filterVersionsByRegEx": "Filter Versions by Regular Expression", "pickExportDir": "选择导出文件夹",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK", "autoExportOnChanges": "数据变更时自动导出",
"filterVersionsByRegEx": "使用正则表达式筛选版本号",
"trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
"dontSortReleasesList": "保持来自 API 的发行顺序",
"reverseSort": "反转排序",
"debugMenu": "调试选项",
"bgTaskStarted": "后台任务已启动 - 详见日志",
"runBgCheckNow": "立即进行后台更新检查",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"installing": "Installing",
"skipUpdateNotifications": "Skip update notifications",
"updatesAvailableNotifChannel": "更新可用",
"appsUpdatedNotifChannel": "应用已更新",
"appsPossiblyUpdatedNotifChannel": "已尝试更新应用",
"errorCheckingUpdatesNotifChannel": "检查更新出错",
"appsRemovedNotifChannel": "应用已删除",
"downloadingXNotifChannel": "正在下载{}",
"completeAppInstallationNotifChannel": "完成应用安装",
"checkingForUpdatesNotifChannel": "正在检查更新",
"removeAppQuestion": { "removeAppQuestion": {
"one": "是否删除应用?", "one": "是否删除应用?",
"other": "是否删除应用?" "other": "是否删除应用?"
@ -308,4 +326,4 @@
"one": "{} 和另外 1 个应用已尝试更新。", "one": "{} 和另外 1 个应用已尝试更新。",
"other": "{} 和另外 {} 个应用已尝试更新。" "other": "{} 和另外 {} 个应用已尝试更新。"
} }
} }

View File

@ -21,16 +21,20 @@ parseDateTimeMMMddCommayyyy(String? dateString) {
class APKPure extends AppSource { class APKPure extends AppSource {
APKPure() { APKPure() {
host = 'apkpure.com'; host = 'apkpure.com';
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);

View File

@ -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

View File

@ -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 = [
[ [
@ -25,7 +26,7 @@ class FDroid extends AppSource {
], ],
[ [
GeneratedFormSwitch('trySelectingSuggestedVersionCode', GeneratedFormSwitch('trySelectingSuggestedVersionCode',
label: tr('trySelectingSuggestedVersionCode'), defaultValue: true) label: tr('trySelectingSuggestedVersionCode'))
], ],
[ [
GeneratedFormSwitch('autoSelectHighestVersionCode', GeneratedFormSwitch('autoSelectHighestVersionCode',
@ -57,81 +58,6 @@ class FDroid extends AppSource {
return Uri.parse(standardUrl).pathSegments.last; return Uri.parse(standardUrl).pathSegments.last;
} }
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
Response res, String apkUrlPrefix, String standardUrl,
{bool autoSelectHighestVersionCode = false,
bool trySelectingSuggestedVersionCode = false,
String? filterVersionsByRegEx}) {
if (res.statusCode == 200) {
var response = jsonDecode(res.body);
List<dynamic> releases = response['packages'] ?? [];
if (releases.isEmpty) {
throw NoReleasesError();
}
String? version;
Iterable<dynamic> releaseChoices = [];
// Grab the versionCode suggested if the user chose to do that
// Only do so at this stage if the user has no release filter
if (trySelectingSuggestedVersionCode &&
response['suggestedVersionCode'] != null &&
filterVersionsByRegEx == null) {
var suggestedReleases = releases.where((element) =>
element['versionCode'] == response['suggestedVersionCode']);
if (suggestedReleases.isNotEmpty) {
releaseChoices = suggestedReleases;
version = suggestedReleases.first['versionName'];
}
}
// Apply the release filter if any
if (filterVersionsByRegEx != null) {
version = null;
releaseChoices = [];
for (var i = 0; i < releases.length; i++) {
if (RegExp(filterVersionsByRegEx)
.hasMatch(releases[i]['versionName'])) {
version = releases[i]['versionName'];
}
}
if (version == null) {
throw NoVersionError();
}
}
// Default to the highest version
version ??= releases[0]['versionName'];
if (version == null) {
throw NoVersionError();
}
// If a suggested release was not already picked, pick all those with the selected version
if (releaseChoices.isEmpty) {
releaseChoices =
releases.where((element) => element['versionName'] == version);
}
// For the remaining releases, use the toggles to auto-select one if possible
if (releaseChoices.length > 1) {
if (autoSelectHighestVersionCode) {
releaseChoices = [releaseChoices.first];
} else if (trySelectingSuggestedVersionCode &&
response['suggestedVersionCode'] != null) {
var suggestedReleases = releaseChoices.where((element) =>
element['versionCode'] == response['suggestedVersionCode']);
if (suggestedReleases.isNotEmpty) {
releaseChoices = suggestedReleases;
}
}
}
if (releaseChoices.isEmpty) {
throw NoReleasesError();
}
List<String> apkUrls = releaseChoices
.map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
.toList();
return APKDetails(version, getApkUrlsFromUrls(apkUrls),
AppNames(name, Uri.parse(standardUrl).pathSegments.last));
} else {
throw getObtainiumHttpError(res);
}
}
@override @override
Future<APKDetails> getLatestAPKDetails( Future<APKDetails> getLatestAPKDetails(
String standardUrl, String standardUrl,
@ -143,6 +69,7 @@ class FDroid extends AppSource {
await sourceRequest('https://$host/api/v1/packages/$appId'), await sourceRequest('https://$host/api/v1/packages/$appId'),
'https://$host/repo/$appId', 'https://$host/repo/$appId',
standardUrl, standardUrl,
name,
autoSelectHighestVersionCode: autoSelectHighestVersionCode:
additionalSettings['autoSelectHighestVersionCode'] == true, additionalSettings['autoSelectHighestVersionCode'] == true,
trySelectingSuggestedVersionCode: trySelectingSuggestedVersionCode:
@ -185,3 +112,78 @@ class FDroid extends AppSource {
} }
} }
} }
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
Response res, String apkUrlPrefix, String standardUrl, String sourceName,
{bool autoSelectHighestVersionCode = false,
bool trySelectingSuggestedVersionCode = false,
String? filterVersionsByRegEx}) {
if (res.statusCode == 200) {
var response = jsonDecode(res.body);
List<dynamic> releases = response['packages'] ?? [];
if (releases.isEmpty) {
throw NoReleasesError();
}
String? version;
Iterable<dynamic> releaseChoices = [];
// Grab the versionCode suggested if the user chose to do that
// Only do so at this stage if the user has no release filter
if (trySelectingSuggestedVersionCode &&
response['suggestedVersionCode'] != null &&
filterVersionsByRegEx == null) {
var suggestedReleases = releases.where((element) =>
element['versionCode'] == response['suggestedVersionCode']);
if (suggestedReleases.isNotEmpty) {
releaseChoices = suggestedReleases;
version = suggestedReleases.first['versionName'];
}
}
// Apply the release filter if any
if (filterVersionsByRegEx != null) {
version = null;
releaseChoices = [];
for (var i = 0; i < releases.length; i++) {
if (RegExp(filterVersionsByRegEx)
.hasMatch(releases[i]['versionName'])) {
version = releases[i]['versionName'];
}
}
if (version == null) {
throw NoVersionError();
}
}
// Default to the highest version
version ??= releases[0]['versionName'];
if (version == null) {
throw NoVersionError();
}
// If a suggested release was not already picked, pick all those with the selected version
if (releaseChoices.isEmpty) {
releaseChoices =
releases.where((element) => element['versionName'] == version);
}
// For the remaining releases, use the toggles to auto-select one if possible
if (releaseChoices.length > 1) {
if (autoSelectHighestVersionCode) {
releaseChoices = [releaseChoices.first];
} else if (trySelectingSuggestedVersionCode &&
response['suggestedVersionCode'] != null) {
var suggestedReleases = releaseChoices.where((element) =>
element['versionCode'] == response['suggestedVersionCode']);
if (suggestedReleases.isNotEmpty) {
releaseChoices = suggestedReleases;
}
}
}
if (releaseChoices.isEmpty) {
throw NoReleasesError();
}
List<String> apkUrls = releaseChoices
.map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
.toList();
return APKDetails(version, getApkUrlsFromUrls(apkUrls),
AppNames(sourceName, Uri.parse(standardUrl).pathSegments.last));
} else {
throw getObtainiumHttpError(res);
}
}

View File

@ -7,6 +7,8 @@ import 'package:obtainium/providers/source_provider.dart';
class FDroidRepo extends AppSource { class FDroidRepo extends AppSource {
FDroidRepo() { FDroidRepo() {
name = tr('fdroidThirdPartyRepo'); name = tr('fdroidThirdPartyRepo');
canSearch = true;
neverAutoSelect = true;
additionalSourceAppSpecificSettingFormItems = [ additionalSourceAppSpecificSettingFormItems = [
[ [
@ -22,12 +24,85 @@ class FDroidRepo extends AppSource {
]; ];
} }
String removeQueryParamsFromUrl(String url, {List<String> keep = const []}) {
var uri = Uri.parse(url);
Map<String, dynamic> resultParams = {};
uri.queryParameters.forEach((key, value) {
if (keep.contains(key)) {
resultParams[key] = value;
}
});
url = uri.replace(queryParameters: resultParams).toString();
if (url.endsWith('?')) {
url = url.substring(0, url.length - 1);
}
return url;
}
@override
String sourceSpecificStandardizeURL(String url) {
var standardUri = Uri.parse(url);
var pathSegments = standardUri.pathSegments;
if (pathSegments.last == 'index.xml') {
pathSegments.removeLast();
standardUri = standardUri.replace(path: pathSegments.join('/'));
}
return removeQueryParamsFromUrl(standardUri.toString(), keep: ['appId']);
}
@override
Future<Map<String, List<String>>> search(String query,
{Map<String, dynamic> querySettings = const {}}) async {
query = removeQueryParamsFromUrl(standardizeUrl(query));
var res = await sourceRequest('$query/index.xml');
if (res.statusCode == 200) {
var body = parse(res.body);
Map<String, List<String>> results = {};
body.querySelectorAll('application').toList().forEach((app) {
String appId = app.attributes['id']!;
results['$query?appId=$appId'] = [
app.querySelector('name')?.innerHtml ?? appId,
app.querySelector('desc')?.innerHtml ?? ''
];
});
return results;
} else {
throw getObtainiumHttpError(res);
}
}
@override
App endOfGetAppChanges(App app) {
var uri = Uri.parse(app.url);
String? appId;
if (!isTempId(app)) {
appId = app.id;
} else if (uri.queryParameters['appId'] != null) {
appId = uri.queryParameters['appId'];
}
if (appId != null) {
app.url = uri
.replace(
queryParameters: Map.fromEntries(
[...uri.queryParameters.entries, MapEntry('appId', appId)]))
.toString();
app.additionalSettings['appIdOrName'] = appId;
app.id = appId;
}
return app;
}
@override @override
Future<APKDetails> getLatestAPKDetails( Future<APKDetails> getLatestAPKDetails(
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
String? appIdOrName = additionalSettings['appIdOrName']; String? appIdOrName = additionalSettings['appIdOrName'];
var standardUri = Uri.parse(standardUrl);
if (standardUri.queryParameters['appId'] != null) {
appIdOrName = standardUri.queryParameters['appId'];
}
standardUrl = removeQueryParamsFromUrl(standardUrl);
bool pickHighestVersionCode = additionalSettings['pickHighestVersionCode']; bool pickHighestVersionCode = additionalSettings['pickHighestVersionCode'];
if (appIdOrName == null) { if (appIdOrName == null) {
throw NoReleasesError(); throw NoReleasesError();
@ -41,7 +116,7 @@ class FDroidRepo extends AppSource {
if (foundApps.isEmpty) { if (foundApps.isEmpty) {
foundApps = body.querySelectorAll('application').where((element) { foundApps = body.querySelectorAll('application').where((element) {
return element.querySelector('name')?.innerHtml.toLowerCase() == return element.querySelector('name')?.innerHtml.toLowerCase() ==
appIdOrName.toLowerCase(); appIdOrName!.toLowerCase();
}).toList(); }).toList();
} }
if (foundApps.isEmpty) { if (foundApps.isEmpty) {
@ -50,7 +125,7 @@ class FDroidRepo extends AppSource {
.querySelector('name') .querySelector('name')
?.innerHtml ?.innerHtml
.toLowerCase() .toLowerCase()
.contains(appIdOrName.toLowerCase()) ?? .contains(appIdOrName!.toLowerCase()) ??
false; false;
}).toList(); }).toList();
} }
@ -58,8 +133,9 @@ class FDroidRepo extends AppSource {
throw ObtainiumError(tr('appWithIdOrNameNotFound')); throw ObtainiumError(tr('appWithIdOrNameNotFound'));
} }
var authorName = body.querySelector('repo')?.attributes['name'] ?? name; var authorName = body.querySelector('repo')?.attributes['name'] ?? name;
var appName = String appId = foundApps[0].attributes['id']!;
foundApps[0].querySelector('name')?.innerHtml ?? appIdOrName; foundApps[0].querySelector('name')?.innerHtml ?? appId;
var appName = foundApps[0].querySelector('name')?.innerHtml ?? appId;
var releases = foundApps[0].querySelectorAll('package'); var releases = foundApps[0].querySelectorAll('package');
String? latestVersion = releases[0].querySelector('version')?.innerHtml; String? latestVersion = releases[0].querySelector('version')?.innerHtml;
String? added = releases[0].querySelector('added')?.innerHtml; String? added = releases[0].querySelector('added')?.innerHtml;

View File

@ -22,7 +22,6 @@ class GitHub extends AppSource {
label: tr('githubPATLabel'), label: tr('githubPATLabel'),
password: true, password: true,
required: false, required: false,
hint: tr('githubPATFormat'),
belowWidgets: [ belowWidgets: [
const SizedBox( const SizedBox(
height: 4, height: 4,
@ -73,9 +72,10 @@ class GitHub extends AppSource {
} }
]) ])
], ],
[GeneratedFormSwitch('verifyLatestTag', label: tr('verifyLatestTag'))],
[ [
GeneratedFormSwitch('verifyLatestTag', GeneratedFormSwitch('dontSortReleasesList',
label: tr('verifyLatestTag'), defaultValue: false) label: tr('dontSortReleasesList'))
] ]
]; ];
@ -231,6 +231,8 @@ class GitHub extends AppSource {
? additionalSettings['filterReleaseNotesByRegEx'] ? additionalSettings['filterReleaseNotesByRegEx']
: null; : null;
bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true; bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true;
bool dontSortReleasesList =
additionalSettings['dontSortReleasesList'] == true;
String? latestTag; String? latestTag;
if (verifyLatestTag) { if (verifyLatestTag) {
var temp = requestUrl.split('?'); var temp = requestUrl.split('?');
@ -266,32 +268,36 @@ class GitHub extends AppSource {
rel?['published_at'] != null rel?['published_at'] != null
? DateTime.parse(rel['published_at']) ? DateTime.parse(rel['published_at'])
: null; : null;
releases.sort((a, b) { if (dontSortReleasesList) {
// See #478 and #534 releases = releases.reversed.toList();
if (a == b) { } else {
return 0; releases.sort((a, b) {
} else if (a == null) { // See #478 and #534
return -1; if (a == b) {
} else if (b == null) { return 0;
return 1; } else if (a == null) {
} else { return -1;
var nameA = a['tag_name'] ?? a['name']; } else if (b == null) {
var nameB = b['tag_name'] ?? b['name']; return 1;
var stdFormats = findStandardFormatsForVersion(nameA, true)
.intersection(findStandardFormatsForVersion(nameB, true));
if (stdFormats.isNotEmpty) {
var reg = RegExp(stdFormats.first);
var matchA = reg.firstMatch(nameA);
var matchB = reg.firstMatch(nameB);
return compareAlphaNumeric(
(nameA as String).substring(matchA!.start, matchA.end),
(nameB as String).substring(matchB!.start, matchB.end));
} else { } else {
return (getReleaseDateFromRelease(a) ?? DateTime(1)) var nameA = a['tag_name'] ?? a['name'];
.compareTo(getReleaseDateFromRelease(b) ?? DateTime(0)); var nameB = b['tag_name'] ?? b['name'];
var stdFormats = findStandardFormatsForVersion(nameA, true)
.intersection(findStandardFormatsForVersion(nameB, true));
if (stdFormats.isNotEmpty) {
var reg = RegExp(stdFormats.first);
var matchA = reg.firstMatch(nameA);
var matchB = reg.firstMatch(nameB);
return compareAlphaNumeric(
(nameA as String).substring(matchA!.start, matchA.end),
(nameB as String).substring(matchB!.start, matchB.end));
} else {
return (getReleaseDateFromRelease(a) ?? DateTime(1))
.compareTo(getReleaseDateFromRelease(b) ?? DateTime(0));
}
} }
} });
}); }
if (latestTag != null && if (latestTag != null &&
releases.isNotEmpty && releases.isNotEmpty &&
latestTag != latestTag !=

View File

@ -22,7 +22,7 @@ String ensureAbsoluteUrl(String ambiguousUrl, Uri referenceAbsoluteUrl) {
} else if (ambiguousUrl.split('/').where((e) => e.isNotEmpty).length == 1) { } else if (ambiguousUrl.split('/').where((e) => e.isNotEmpty).length == 1) {
return '${referenceAbsoluteUrl.origin}/${currPathSegments.join('/')}/$ambiguousUrl'; return '${referenceAbsoluteUrl.origin}/${currPathSegments.join('/')}/$ambiguousUrl';
} else { } else {
return '${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - 1).join('/')}/$ambiguousUrl'; return '${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - (currPathSegments.last.contains('.') ? 1 : 0)).join('/')}/$ambiguousUrl';
} }
} }
@ -93,6 +93,7 @@ class HTML extends AppSource {
GeneratedFormSwitch('sortByFileNamesNotLinks', GeneratedFormSwitch('sortByFileNamesNotLinks',
label: tr('sortByFileNamesNotLinks')) label: tr('sortByFileNamesNotLinks'))
], ],
[GeneratedFormSwitch('reverseSort', label: tr('reverseSort'))],
[ [
GeneratedFormTextField('customLinkFilterRegex', GeneratedFormTextField('customLinkFilterRegex',
label: tr('customLinkFilterRegex'), label: tr('customLinkFilterRegex'),
@ -107,7 +108,7 @@ class HTML extends AppSource {
[ [
GeneratedFormTextField('intermediateLinkRegex', GeneratedFormTextField('intermediateLinkRegex',
label: tr('intermediateLinkRegex'), label: tr('intermediateLinkRegex'),
hint: '([0-9]+\.)*[0-9]+/\$', hint: '([0-9]+.)*[0-9]+/\$',
required: false, required: false,
additionalValidators: [(value) => regExValidator(value)]) additionalValidators: [(value) => regExValidator(value)])
], ],
@ -116,20 +117,26 @@ class HTML extends AppSource {
label: tr('versionExtractionRegEx'), label: tr('versionExtractionRegEx'),
required: false, required: false,
additionalValidators: [(value) => regExValidator(value)]), additionalValidators: [(value) => regExValidator(value)]),
],
[
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);
} }
]) ])
],
[
GeneratedFormSwitch('versionExtractWholePage',
label: tr('versionExtractWholePage'))
] ]
]; ];
overrideVersionDetectionFormDefault('noVersionDetection', overrideVersionDetectionFormDefault('noVersionDetection',
@ -195,6 +202,9 @@ class HTML extends AppSource {
? compareAlphaNumeric(a.split('/').where((e) => e.isNotEmpty).last, ? compareAlphaNumeric(a.split('/').where((e) => e.isNotEmpty).last,
b.split('/').where((e) => e.isNotEmpty).last) b.split('/').where((e) => e.isNotEmpty).last)
: compareAlphaNumeric(a, b)); : compareAlphaNumeric(a, b));
if (additionalSettings['reverseSort'] == true) {
links = links.reversed.toList();
}
if ((additionalSettings['apkFilterRegEx'] as String?)?.isNotEmpty == if ((additionalSettings['apkFilterRegEx'] as String?)?.isNotEmpty ==
true) { true) {
var reg = RegExp(additionalSettings['apkFilterRegEx']); var reg = RegExp(additionalSettings['apkFilterRegEx']);
@ -208,12 +218,17 @@ class HTML extends AppSource {
var versionExtractionRegEx = var versionExtractionRegEx =
additionalSettings['versionExtractionRegEx'] as String?; additionalSettings['versionExtractionRegEx'] as String?;
if (versionExtractionRegEx?.isNotEmpty == true) { if (versionExtractionRegEx?.isNotEmpty == true) {
var match = RegExp(versionExtractionRegEx!).allMatches(rel); var match = RegExp(versionExtractionRegEx!).allMatches(
res.body.split('\r\n').join('\n').split('\n').join('\\n'));
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();
} }

View File

@ -6,16 +6,22 @@ class IzzyOnDroid extends AppSource {
late FDroid fd; late FDroid fd;
IzzyOnDroid() { IzzyOnDroid() {
host = 'android.izzysoft.de'; host = 'izzysoft.de';
fd = FDroid(); fd = FDroid();
additionalSourceAppSpecificSettingFormItems = additionalSourceAppSpecificSettingFormItems =
fd.additionalSourceAppSpecificSettingFormItems; fd.additionalSourceAppSpecificSettingFormItems;
allowSubDomains = true;
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://$host/repo/apk/[^/]+'); RegExp standardUrlRegExA = RegExp('^https?://android.$host/repo/apk/[^/]+');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegExA.firstMatch(url.toLowerCase());
if (match == null) {
RegExp standardUrlRegExB =
RegExp('^https?://apt.$host/fdroid/index/apk/[^/]+');
match = standardUrlRegExB.firstMatch(url.toLowerCase());
}
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
@ -34,11 +40,12 @@ class IzzyOnDroid extends AppSource {
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
String? appId = await tryInferringAppId(standardUrl); String? appId = await tryInferringAppId(standardUrl);
return fd.getAPKUrlsFromFDroidPackagesAPIResponse( return getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest( await sourceRequest(
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'), 'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),
'https://android.izzysoft.de/frepo/$appId', 'https://android.izzysoft.de/frepo/$appId',
standardUrl, standardUrl,
name,
autoSelectHighestVersionCode: autoSelectHighestVersionCode:
additionalSettings['autoSelectHighestVersionCode'] == true, additionalSettings['autoSelectHighestVersionCode'] == true,
trySelectingSuggestedVersionCode: trySelectingSuggestedVersionCode:

View File

@ -8,6 +8,7 @@ class Uptodown extends AppSource {
Uptodown() { Uptodown() {
host = 'uptodown.com'; host = 'uptodown.com';
allowSubDomains = true; allowSubDomains = true;
naiveStandardVersionDetection = true;
} }
@override @override
@ -34,7 +35,7 @@ class Uptodown extends AppSource {
var html = parse(res.body); var html = parse(res.body);
String? version = html.querySelector('div.version')?.innerHtml; String? version = html.querySelector('div.version')?.innerHtml;
String? apkUrl = 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? name = html.querySelector('#detail-app-name')?.innerHtml.trim();
String? author = html.querySelector('#author-link')?.innerHtml.trim(); String? author = html.querySelector('#author-link')?.innerHtml.trim();
var detailElements = html.querySelectorAll('#technical-information td'); var detailElements = html.querySelectorAll('#technical-information td');
@ -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;
}
} }

View File

@ -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);
}
} }
} }

View File

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:android_package_installer/android_package_installer.dart'; import 'package:android_package_installer/android_package_installer.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -65,30 +67,43 @@ 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); if (error is SocketException) {
error = error.message;
}
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, {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 @override
String toString() { String toString() => idsByErrorString.entries
String finalString = ''; .map((e) => errorsAppsString(e.key, e.value))
for (var e in content.keys) { .join('\n\n');
finalString += '$e: ${content[e].toString()}\n\n';
}
return finalString;
}
} }
showError(dynamic e, BuildContext context) { showMessage(dynamic e, BuildContext context, {bool isError = false}) {
Provider.of<LogsProvider>(context, listen: false) Provider.of<LogsProvider>(context, listen: false)
.add(e.toString(), level: LogLevels.error); .add(e.toString(), level: isError ? LogLevels.error : LogLevels.info);
if (e is String || (e is ObtainiumError && !e.unexpected)) { if (e is String || (e is ObtainiumError && !e.unexpected)) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())), SnackBar(content: Text(e.toString())),
@ -100,8 +115,8 @@ showError(dynamic e, BuildContext context) {
return AlertDialog( return AlertDialog(
scrollable: true, scrollable: true,
title: Text(e is MultiAppMultiError title: Text(e is MultiAppMultiError
? tr('someErrors') ? tr(isError ? 'someErrors' : 'updates')
: tr('unexpectedError')), : tr(isError ? 'unexpectedError' : 'unknown')),
content: GestureDetector( content: GestureDetector(
onLongPress: () { onLongPress: () {
Clipboard.setData(ClipboardData(text: e.toString())); Clipboard.setData(ClipboardData(text: e.toString()));
@ -122,6 +137,10 @@ showError(dynamic e, BuildContext context) {
} }
} }
showError(dynamic e, BuildContext context) {
showMessage(e, context, isError: true);
}
String list2FriendlyString(List<String> list) { String list2FriendlyString(List<String> list) {
return list.length == 2 return list.length == 2
? '${list[0]} ${tr('and')} ${list[1]}' ? '${list[0]} ${tr('and')} ${list[1]}'

View File

@ -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.17'; const String currentVersion = '0.14.29';
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
@ -27,7 +27,7 @@ const int bgUpdateCheckAlarmId = 666;
List<MapEntry<Locale, String>> supportedLocales = const [ List<MapEntry<Locale, String>> supportedLocales = const [
MapEntry(Locale('en'), 'English'), MapEntry(Locale('en'), 'English'),
MapEntry(Locale('zh'), '汉语'), MapEntry(Locale('zh'), '简体中文'),
MapEntry(Locale('it'), 'Italiano'), MapEntry(Locale('it'), 'Italiano'),
MapEntry(Locale('ja'), '日本語'), MapEntry(Locale('ja'), '日本語'),
MapEntry(Locale('hu'), 'Magyar'), MapEntry(Locale('hu'), 'Magyar'),
@ -38,7 +38,8 @@ List<MapEntry<Locale, String>> supportedLocales = const [
MapEntry(Locale('pl'), 'Polski'), MapEntry(Locale('pl'), 'Polski'),
MapEntry(Locale('ru'), 'Русский язык'), MapEntry(Locale('ru'), 'Русский язык'),
MapEntry(Locale('bs'), 'Bosanski'), MapEntry(Locale('bs'), 'Bosanski'),
// MapEntry(Locale('br'), 'Brasileiro'), MapEntry(Locale('pt'), 'Brasileiro'),
MapEntry(Locale('cs'), 'Česky'),
]; ];
const fallbackLocale = Locale('en'); const fallbackLocale = Locale('en');
const localeDir = 'assets/translations'; const localeDir = 'assets/translations';

View File

@ -153,7 +153,7 @@ class _AddAppPageState extends State<AddAppPage> {
overrideSource: pickedSourceOverride, overrideSource: pickedSourceOverride,
inferAppIdIfOptional: inferAppIdIfOptional); inferAppIdIfOptional: inferAppIdIfOptional);
// Only download the APK here if you need to for the package ID // Only download the APK here if you need to for the package ID
if (sourceProvider.isTempId(app) && if (isTempId(app) &&
app.additionalSettings['trackOnly'] != true) { app.additionalSettings['trackOnly'] != true) {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
var apkUrl = await appsProvider.confirmApkUrl(app, context); var apkUrl = await appsProvider.confirmApkUrl(app, context);

View File

@ -292,7 +292,7 @@ class _AppPageState extends State<AppPage> {
if (source?.enforceTrackOnly == true) { if (source?.enforceTrackOnly == true) {
app.app.additionalSettings['trackOnly'] = true; app.app.additionalSettings['trackOnly'] = true;
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
showError(tr('appsFromSourceAreTrackOnly'), context); showMessage(tr('appsFromSourceAreTrackOnly'), context);
} }
if (app.app.additionalSettings['versionDetection'] == if (app.app.additionalSettings['versionDetection'] ==
'releaseDateAsVersion') { 'releaseDateAsVersion') {
@ -343,7 +343,7 @@ class _AppPageState extends State<AppPage> {
); );
if (app?.app.installedVersion != null && !trackOnly) { if (app?.app.installedVersion != null && !trackOnly) {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
showError(tr('appsUpdated'), context); showMessage(tr('appsUpdated'), context);
} }
if (res.isNotEmpty && mounted) { if (res.isNotEmpty && mounted) {
Navigator.of(context).pop(); Navigator.of(context).pop();

View File

@ -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(() {
@ -566,7 +566,7 @@ class AppsPageState extends State<AppsPage> {
.toInt() .toInt()
.toString() .toString()
]) ])
: tr('pleaseWait'), : tr('installing'),
textAlign: (listedApps[index].downloadProgress! >= 0) textAlign: (listedApps[index].downloadProgress! >= 0)
? TextAlign.start ? TextAlign.start
: TextAlign.end, : TextAlign.end,
@ -705,7 +705,7 @@ class AppsPageState extends State<AppsPage> {
return <String>[]; return <String>[];
}).then((value) { }).then((value) {
if (shouldInstallUpdates) { if (shouldInstallUpdates) {
showError(tr('appsUpdated'), context); showMessage(tr('appsUpdated'), context);
} }
}); });
} }
@ -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) {

View File

@ -81,7 +81,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
}); });
appsProvider.addAppsByURL(urls).then((errors) { appsProvider.addAppsByURL(urls).then((errors) {
if (errors.isEmpty) { if (errors.isEmpty) {
showError(tr('importedX', args: [plural('apps', urls.length)]), showMessage(tr('importedX', args: [plural('apps', urls.length)]),
context); context);
} else { } else {
showDialog( showDialog(
@ -102,15 +102,16 @@ 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) {
showError(tr('exportedTo', args: [result]), context); showMessage(tr('exportedTo', args: [result]), context);
} }
}).catchError((e) { }).catchError((e) {
showError(e, context); showError(e, context);
@ -140,7 +141,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
} }
}); });
appsProvider.addMissingCategories(settingsProvider); appsProvider.addMissingCategories(settingsProvider);
showError(tr('importedX', args: [plural('apps', value)]), context); showMessage(tr('importedX', args: [plural('apps', value)]), context);
}); });
} else { } else {
// User canceled the picker // User canceled the picker
@ -215,8 +216,9 @@ class _ImportExportPageState extends State<ImportExportPage> {
var errors = await appsProvider.addAppsByURL(selectedUrls); var errors = await appsProvider.addAppsByURL(selectedUrls);
if (errors.isEmpty) { if (errors.isEmpty) {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
showError( showMessage(
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
@ -272,8 +274,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
var errors = await appsProvider.addAppsByURL(selectedUrls); var errors = await appsProvider.addAppsByURL(selectedUrls);
if (errors.isEmpty) { if (errors.isEmpty) {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
showError( showMessage(
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)

View File

@ -535,7 +535,7 @@ class _SettingsPageState extends State<SettingsPage> {
onPressed: () { onPressed: () {
context.read<LogsProvider>().get().then((logs) { context.read<LogsProvider>().get().then((logs) {
if (logs.isEmpty) { if (logs.isEmpty) {
showError(ObtainiumError(tr('noLogs')), context); showMessage(ObtainiumError(tr('noLogs')), context);
} else { } else {
showDialog( showDialog(
context: context, context: context,
@ -558,7 +558,7 @@ class _SettingsPageState extends State<SettingsPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Flexible(child: Text('Debug Menu')), Flexible(child: Text(tr('debugMenu'))),
Switch( Switch(
value: settingsProvider.showDebugOpts, value: settingsProvider.showDebugOpts,
onChanged: (value) { onChanged: (value) {
@ -577,12 +577,9 @@ class _SettingsPageState extends State<SettingsPage> {
const Duration(seconds: 0), const Duration(seconds: 0),
bgUpdateCheckAlarmId + 200, bgUpdateCheckAlarmId + 200,
bgUpdateCheck); bgUpdateCheck);
showError( showMessage(tr('bgTaskStarted'), context);
'Background task started - check logs.',
context);
}, },
child: child: Text(tr('runBgCheckNow')))
const Text('Run Background Update Check Now'))
], ],
), ),
]), ]),

View File

@ -263,25 +263,29 @@ class AppsProvider with ChangeNotifier {
return downloadedFile; return downloadedFile;
} }
Future<File> handleAPKIDChange(App app, PackageInfo newInfo, Future<File> handleAPKIDChange(App app, PackageInfo? newInfo,
File downloadedFile, String downloadUrl) async { File downloadedFile, String downloadUrl) async {
// If the APK package ID is different from the App ID, it is either new (using a placeholder ID) or the ID has changed // If the APK package ID is different from the App ID, it is either new (using a placeholder ID) or the ID has changed
// The former case should be handled (give the App its real ID), the latter is a security issue // The former case should be handled (give the App its real ID), the latter is a security issue
if (app.id != newInfo.packageName) { var isTempIdBool = isTempId(app);
var isTempId = SourceProvider().isTempId(app); if (newInfo != null) {
if (apps[app.id] != null && !isTempId && !app.allowIdChange) { if (app.id != newInfo.packageName) {
throw IDChangedError(newInfo.packageName!); if (apps[app.id] != null && !isTempIdBool && !app.allowIdChange) {
} throw IDChangedError(newInfo.packageName!);
var idChangeWasAllowed = app.allowIdChange; }
app.allowIdChange = false; var idChangeWasAllowed = app.allowIdChange;
var originalAppId = app.id; app.allowIdChange = false;
app.id = newInfo.packageName!; var originalAppId = app.id;
downloadedFile = downloadedFile.renameSync( app.id = newInfo.packageName!;
'${downloadedFile.parent.path}/${app.id}-${downloadUrl.hashCode}.${downloadedFile.path.split('.').last}'); downloadedFile = downloadedFile.renameSync(
if (apps[originalAppId] != null) { '${downloadedFile.parent.path}/${app.id}-${downloadUrl.hashCode}.${downloadedFile.path.split('.').last}');
await removeApps([originalAppId]); if (apps[originalAppId] != null) {
await saveApps([app], onlyIfExists: !isTempId && !idChangeWasAllowed); await removeApps([originalAppId]);
await saveApps([app], onlyIfExists: !isTempIdBool && !idChangeWasAllowed);
}
} }
} else if (isTempIdBool) {
throw ObtainiumError('Could not get ID from APK');
} }
return downloadedFile; return downloadedFile;
} }
@ -344,7 +348,7 @@ class AppsProvider with ChangeNotifier {
await pm.getPackageArchiveInfo(archiveFilePath: apks.first.path); await pm.getPackageArchiveInfo(archiveFilePath: apks.first.path);
} }
downloadedFile = downloadedFile =
await handleAPKIDChange(app, newInfo!, downloadedFile, downloadUrl); await handleAPKIDChange(app, newInfo, downloadedFile, downloadUrl);
// Delete older versions of the file if any // Delete older versions of the file if any
for (var file in downloadedFile.parent.listSync()) { for (var file in downloadedFile.parent.listSync()) {
var fn = file.path.split('/').last; var fn = file.path.split('/').last;
@ -449,7 +453,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 +461,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 +681,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 +713,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 +736,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 +756,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 +764,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 +784,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 +1072,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 +1081,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;
} }
@ -1116,10 +1140,8 @@ class AppsProvider with ChangeNotifier {
return null; return null;
} }
if (exportDir == null) { if (exportDir == null) {
logs.add('Skipping auto-export as dir is not set.');
return null; return null;
} }
logs.add('Started auto-export.');
var files = await saf var files = await saf
.listFiles(exportDir, columns: [saf.DocumentFileColumn.id]) .listFiles(exportDir, columns: [saf.DocumentFileColumn.id])
.where((f) => f.uri.pathSegments.last.endsWith('-auto.json')) .where((f) => f.uri.pathSegments.last.endsWith('-auto.json'))
@ -1128,7 +1150,6 @@ class AppsProvider with ChangeNotifier {
for (var f in files) { for (var f in files) {
saf.delete(f.uri); saf.delete(f.uri);
} }
logs.add('Previous auto-export deleted.');
} }
} }
if (exportDir == null || pickOnly) { if (exportDir == null || pickOnly) {
@ -1302,22 +1323,21 @@ 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 (if empty - which is the default - all pending updates are taken)
///
/// @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 (in parallel).
/// 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 and it cannot be installed silently, the user is notified of the available update.
/// 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 any errors, the task is run again for the remaining apps after a few minutes (based on the error with the longest retry interval).
/// Any app that has reached it's retry limit, the user is notified that it could not be checked.
/// ///
/// 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 pending silent updates are downloaded and installed in the background (serially - one at a time).
/// 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 offending app is moved to the back of the line of remaining apps, and the task is retried.
/// If an app repeatedly fails to install up to its retry limit, the user is notified.
/// ///
/// In either mode, if the function fails after the maximum number of tries, the user is notified.
@pragma('vm:entry-point') @pragma('vm:entry-point')
Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async { Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
@ -1353,94 +1373,153 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
(<List<MapEntry<String, int>>>[])) (<List<MapEntry<String, int>>>[]))
]; ];
bool installMode = toCheck.isEmpty && var netResult = await (Connectivity().checkConnectivity());
toInstall.isNotEmpty; // Task is either in update mode or install mode
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
logs.add( logs.add(
'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)
// 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
// If toRetry is empty, we take care of schedule another task that will run in install mode (toCheck is empty)
// Init. vars.
List<App> updates = []; // All updates found (silent and non-silent)
List<App> toNotify =
[]; // All non-silent updates that the user will be notified about
List<MapEntry<String, int>> toRetry =
[]; // All apps that got errors while checking
var retryAfterXSeconds =
0; // How long to wait until the next attempt (if there are errors)
MultiAppMultiError?
errors; // All errors including those that will lead to a retry
MultiAppMultiError toThrow =
MultiAppMultiError(); // All errors that will not lead to a retry, just a notification
CheckingUpdatesNotification notif = CheckingUpdatesNotification(
plural('apps', toCheck.length)); // The notif. to show while checking
// Set a bool for when we're no on wifi/wired and the user doesn't want to download apps in that state
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
List<App> toNotify = [];
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 based on max retry count per app
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)); // Next task interval is based on the error with the longest retry time
} var minRetryIntervalForThisApp = err is RateLimitError
} ? (err.remainingMinutes * 60)
if (i == (toCheck.length - 1)) { : e is ClientException
didCompleteChecking = true; ? (15 * 60)
} : pow(toCheckApp.value + 1, 2).toInt();
} catch (e) { if (minRetryIntervalForThisApp > retryAfterXSeconds) {
// 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 retryAfterXSeconds = minRetryIntervalForThisApp;
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, err, appName: errors?.appIdNames[key]);
} }
} });
} else {
// We don't expect to ever get here in any situation so no need to catch (but log it in case)
logs.add('Fatal error in BG update task: ${e.toString()}');
rethrow;
} }
} finally { } finally {
if (toNotify.isNotEmpty) { notificationsProvider.cancel(notif.id);
notificationsProvider.notify(UpdateNotification(toNotify)); }
// Filter out updates that will be installed silently (the rest go into toNotify)
for (var i = 0; i < updates.length; i++) {
if (networkRestricted ||
!(await appsProvider.canInstallSilently(updates[i]))) {
if (updates[i].additionalSettings['skipUpdateNotifications'] != true) {
toNotify.add(updates[i]);
}
} }
} }
// 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 (grouped by error string)
if (toThrow.rawErrors.isNotEmpty) {
for (var element in toThrow.idsByErrorString.entries) {
notificationsProvider.notify(ErrorCheckingUpdatesNotification(
errors!.errorsAppsString(element.key, element.value),
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 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(
@ -1451,11 +1530,19 @@ 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) {
logs.add('BG install task $taskId: Done.');
} }
} else { } else {
// If in install mode... // In install mode...
// If you haven't explicitly been given updates to install (which is the case for new tasks), grab all available silent updates
if (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));
}
}
}
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) {
@ -1507,9 +1594,9 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
.notify(ErrorCheckingUpdatesNotification(e.toString())); .notify(ErrorCheckingUpdatesNotification(e.toString()));
} }
} }
if (didCompleteInstalling) { }
logs.add('BG install task $taskId: Done.'); if (didCompleteInstalling || toInstall.isEmpty) {
} logs.add('BG install task $taskId: Done.');
} }
} }
} }

View File

@ -28,7 +28,7 @@ class UpdateNotification extends ObtainiumNotification {
tr('updatesAvailable'), tr('updatesAvailable'),
'', '',
'UPDATES_AVAILABLE', 'UPDATES_AVAILABLE',
tr('updatesAvailable'), tr('updatesAvailableNotifChannel'),
tr('updatesAvailableNotifDescription'), tr('updatesAvailableNotifDescription'),
Importance.max) { Importance.max) {
message = updates.isEmpty message = updates.isEmpty
@ -42,8 +42,14 @@ class UpdateNotification extends ObtainiumNotification {
class SilentUpdateNotification extends ObtainiumNotification { class SilentUpdateNotification extends ObtainiumNotification {
SilentUpdateNotification(List<App> updates, {int? id}) SilentUpdateNotification(List<App> updates, {int? id})
: super(id ?? 3, tr('appsUpdated'), '', 'APPS_UPDATED', tr('appsUpdated'), : super(
tr('appsUpdatedNotifDescription'), Importance.defaultImportance) { id ?? 3,
tr('appsUpdated'),
'',
'APPS_UPDATED',
tr('appsUpdatedNotifChannel'),
tr('appsUpdatedNotifDescription'),
Importance.defaultImportance) {
message = updates.length == 1 message = updates.length == 1
? tr('xWasUpdatedToY', ? tr('xWasUpdatedToY',
args: [updates[0].finalName, updates[0].latestVersion]) args: [updates[0].finalName, updates[0].latestVersion])
@ -59,7 +65,7 @@ class SilentUpdateAttemptNotification extends ObtainiumNotification {
tr('appsPossiblyUpdated'), tr('appsPossiblyUpdated'),
'', '',
'APPS_POSSIBLY_UPDATED', 'APPS_POSSIBLY_UPDATED',
tr('appsPossiblyUpdated'), tr('appsPossiblyUpdatedNotifChannel'),
tr('appsPossiblyUpdatedNotifDescription'), tr('appsPossiblyUpdatedNotifDescription'),
Importance.defaultImportance) { Importance.defaultImportance) {
message = updates.length == 1 message = updates.length == 1
@ -77,15 +83,21 @@ class ErrorCheckingUpdatesNotification extends ObtainiumNotification {
tr('errorCheckingUpdates'), tr('errorCheckingUpdates'),
error, error,
'BG_UPDATE_CHECK_ERROR', 'BG_UPDATE_CHECK_ERROR',
tr('errorCheckingUpdates'), tr('errorCheckingUpdatesNotifChannel'),
tr('errorCheckingUpdatesNotifDescription'), tr('errorCheckingUpdatesNotifDescription'),
Importance.high); Importance.high);
} }
class AppsRemovedNotification extends ObtainiumNotification { class AppsRemovedNotification extends ObtainiumNotification {
AppsRemovedNotification(List<List<String>> namedReasons) AppsRemovedNotification(List<List<String>> namedReasons)
: super(6, tr('appsRemoved'), '', 'APPS_REMOVED', tr('appsRemoved'), : super(
tr('appsRemovedNotifDescription'), Importance.max) { 6,
tr('appsRemoved'),
'',
'APPS_REMOVED',
tr('appsRemovedNotifChannel'),
tr('appsRemovedNotifDescription'),
Importance.max) {
message = ''; message = '';
for (var r in namedReasons) { for (var r in namedReasons) {
message += '${tr('xWasRemovedDueToErrorY', args: [r[0], r[1]])} \n'; message += '${tr('xWasRemovedDueToErrorY', args: [r[0], r[1]])} \n';
@ -101,7 +113,7 @@ class DownloadNotification extends ObtainiumNotification {
tr('downloadingX', args: [appName]), tr('downloadingX', args: [appName]),
'', '',
'APP_DOWNLOADING', 'APP_DOWNLOADING',
tr('downloadingX', args: [tr('app')]), tr('downloadingXNotifChannel', args: [tr('app')]),
tr('downloadNotifDescription'), tr('downloadNotifDescription'),
Importance.low, Importance.low,
onlyAlertOnce: true, onlyAlertOnce: true,
@ -113,7 +125,7 @@ final completeInstallationNotification = ObtainiumNotification(
tr('completeAppInstallation'), tr('completeAppInstallation'),
tr('obtainiumMustBeOpenToInstallApps'), tr('obtainiumMustBeOpenToInstallApps'),
'COMPLETE_INSTALL', 'COMPLETE_INSTALL',
tr('completeAppInstallation'), tr('completeAppInstallationNotifChannel'),
tr('completeAppInstallationNotifDescription'), tr('completeAppInstallationNotifDescription'),
Importance.max); Importance.max);
@ -124,7 +136,7 @@ class CheckingUpdatesNotification extends ObtainiumNotification {
tr('checkingForUpdates'), tr('checkingForUpdates'),
appName, appName,
'BG_UPDATE_CHECK', 'BG_UPDATE_CHECK',
tr('checkingForUpdates'), tr('checkingForUpdatesNotifChannel'),
tr('checkingForUpdatesNotifDescription'), tr('checkingForUpdatesNotifDescription'),
Importance.min); Importance.min);
} }

View File

@ -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,8 @@ abstract class AppSource {
bool changeLogIfAnyIsMarkDown = true; bool changeLogIfAnyIsMarkDown = true;
bool appIdInferIsOptional = false; bool appIdInferIsOptional = false;
bool allowSubDomains = false; bool allowSubDomains = false;
bool naiveStandardVersionDetection = false;
bool neverAutoSelect = false;
AppSource() { AppSource() {
name = runtimeType.toString(); name = runtimeType.toString();
@ -370,6 +373,10 @@ abstract class AppSource {
return null; return null;
} }
App endOfGetAppChanges(App app) {
return app;
}
Future<Response> sourceRequest(String url, Future<Response> sourceRequest(String url,
{bool followRedirects = true, {bool followRedirects = true,
Map<String, dynamic> additionalSettings = Map<String, dynamic> additionalSettings =
@ -440,6 +447,10 @@ abstract class AppSource {
[ [
GeneratedFormSwitch('exemptFromBackgroundUpdates', GeneratedFormSwitch('exemptFromBackgroundUpdates',
label: tr('exemptFromBackgroundUpdates')) label: tr('exemptFromBackgroundUpdates'))
],
[
GeneratedFormSwitch('skipUpdateNotifications',
label: tr('skipUpdateNotifications'))
] ]
]; ];
@ -535,6 +546,11 @@ intValidator(String? value, {bool positive = false}) {
return null; return null;
} }
bool isTempId(App app) {
// return app.id == generateTempID(app.url, app.additionalSettings);
return RegExp('^[0-9]+\$').hasMatch(app.id);
}
class SourceProvider { class SourceProvider {
// Add more source classes here so they are available via the service // Add more source classes here so they are available via the service
List<AppSource> get sources => [ List<AppSource> get sources => [
@ -556,7 +572,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(),
@ -589,7 +605,7 @@ class SourceProvider {
} }
} }
if (source == null) { if (source == null) {
for (var s in sources.where((element) => element.host == null)) { for (var s in sources.where((element) => element.host == null && !element.neverAutoSelect)) {
try { try {
s.sourceSpecificStandardizeURL(url); s.sourceSpecificStandardizeURL(url);
source = s; source = s;
@ -620,11 +636,6 @@ class SourceProvider {
String standardUrl, Map<String, dynamic> additionalSettings) => String standardUrl, Map<String, dynamic> additionalSettings) =>
(standardUrl + additionalSettings.toString()).hashCode.toString(); (standardUrl + additionalSettings.toString()).hashCode.toString();
bool isTempId(App app) {
// return app.id == generateTempID(app.url, app.additionalSettings);
return RegExp('^[0-9]+\$').hasMatch(app.id);
}
Future<App> getApp( Future<App> getApp(
AppSource source, String url, Map<String, dynamic> additionalSettings, AppSource source, String url, Map<String, dynamic> additionalSettings,
{App? currentApp, {App? currentApp,
@ -666,7 +677,7 @@ class SourceProvider {
String apkVersion = apk.version.replaceAll('/', '-'); String apkVersion = apk.version.replaceAll('/', '-');
var name = currentApp != null ? currentApp.name.trim() : ''; var name = currentApp != null ? currentApp.name.trim() : '';
name = name.isNotEmpty ? name : apk.names.name; name = name.isNotEmpty ? name : apk.names.name;
return App( App finalApp = App(
currentApp?.id ?? currentApp?.id ??
((!source.appIdInferIsOptional || ((!source.appIdInferIsOptional ||
(source.appIdInferIsOptional && inferAppIdIfOptional)) (source.appIdInferIsOptional && inferAppIdIfOptional))
@ -692,6 +703,7 @@ class SourceProvider {
source.appIdInferIsOptional && source.appIdInferIsOptional &&
inferAppIdIfOptional // Optional ID inferring may be incorrect - allow correction on first install inferAppIdIfOptional // Optional ID inferring may be incorrect - allow correction on first install
); );
return source.endOfGetAppChanges(finalApp);
} }
// Returns errors in [results, errors] instead of throwing them // Returns errors in [results, errors] instead of throwing them

View File

@ -5,18 +5,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: android_alarm_manager_plus name: android_alarm_manager_plus
sha256: c20d91a9096596f66274bf8172321c278f9cba8091638f80205fe66d31587fa5 sha256: "82fb28c867c4b3dd7e9157728e46426b8916362f977dbba46b949210f00099f4"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.2" version: "3.0.3"
android_intent_plus: android_intent_plus:
dependency: "direct main" dependency: "direct main"
description: description:
name: android_intent_plus name: android_intent_plus
sha256: f72ae20bb37108694f442e7ae6acbd28b453ca62ce86842f6787b784355abfe6 sha256: e1c62bb41c90e15083b7fb84dc327fe90396cc9c1445b55ff1082144fabfb4d9
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "4.0.3"
android_package_installer: android_package_installer:
dependency: "direct main" dependency: "direct main"
description: description:
@ -46,10 +46,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: archive name: archive
sha256: e0902a06f0e00414e4e3438a084580161279f137aeb862274710f29ec10cf01e sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.3.9" version: "3.4.6"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -118,10 +118,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: connectivity_plus name: connectivity_plus
sha256: "77a180d6938f78ca7d2382d2240eb626c0f6a735d0bfdce227d8ffb80f95c48b" sha256: "94d51c6f1299133a2baa4c5c3d2c11ec7d7fb4768dee5c52a56f7d7522fcf70e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "5.0.0"
connectivity_plus_platform_interface: connectivity_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -142,10 +142,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: cross_file name: cross_file
sha256: fd832b5384d0d6da4f6df60b854d33accaaeb63aa9e10e736a87381f08dee2cb sha256: "445db18de832dba8d851e287aff8ccf169bed30d2e94243cb54c7d2f1ed2142c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.3+5" version: "0.3.3+6"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -238,18 +238,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: file name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.4" version: "7.0.0"
file_picker: file_picker:
dependency: "direct main" dependency: "direct main"
description: description:
name: file_picker name: file_picker
sha256: be325344c1f3070354a1d84a231a1ba75ea85d413774ec4bdf444c023342e030 sha256: "903dd4ba13eae7cef64acc480e91bf54c3ddd23b5b90b639c170f3911e489620"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.5.0" version: "6.0.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -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: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.17" 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: f23cfe9af0d49c6b9fd8a8b09f7b3301ca7e346204939b5afef4404d36d2608f sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.0.1" version: "11.1.0"
permission_handler_apple: permission_handler_apple:
dependency: transitive dependency: transitive
description: description:
@ -562,10 +562,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_platform_interface name: permission_handler_platform_interface
sha256: f2343e9fa9c22ae4fd92d4732755bfe452214e7189afcc097380950cf567b4b2 sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.11.5" version: "3.12.0"
permission_handler_windows: permission_handler_windows:
dependency: transitive dependency: transitive
description: description:
@ -586,10 +586,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.2" version: "3.1.3"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -634,10 +634,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: shared_preferences name: shared_preferences
sha256: b7f41bad7e521d205998772545de63ff4e6c97714775902c199353f8bf1511ac sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.1" version: "2.2.2"
shared_preferences_android: shared_preferences_android:
dependency: transitive dependency: transitive
description: description:
@ -658,10 +658,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_linux name: shared_preferences_linux
sha256: c2eb5bf57a2fe9ad6988121609e47d3e07bb3bdca5b6f8444e4cf302428a128a sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.1" version: "2.3.2"
shared_preferences_platform_interface: shared_preferences_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -682,10 +682,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_windows name: shared_preferences_windows
sha256: f763a101313bd3be87edffe0560037500967de9c394a714cd598d945517f694f sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.1" version: "2.3.2"
shared_storage: shared_storage:
dependency: "direct main" dependency: "direct main"
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,26 +903,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_wkwebview name: webview_flutter_wkwebview
sha256: d2f7241849582da80b79acb03bb936422412ce5c0c79fb5f6a1de5421a5aecc4 sha256: "30b9af6bdd457b44c08748b9190d23208b5165357cc2eb57914fee1366c42974"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.7.4" version: "3.9.1"
win32: win32:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "9e82a402b7f3d518fb9c02d0e9ae45952df31b9bf34d77baf19da2de03fc2aaa" sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.7" version: "5.0.9"
win32_registry: win32_registry:
dependency: transitive dependency: transitive
description: description:
name: win32_registry name: win32_registry
sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9 sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.2"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:

View File

@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # 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.17+209 # When changing this, update the tag in main() accordingly version: 0.14.29+221 # 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
@ -49,7 +49,7 @@ dependencies:
permission_handler: ^11.0.0 permission_handler: ^11.0.0
fluttertoast: ^8.0.9 fluttertoast: ^8.0.9
device_info_plus: ^9.0.0 device_info_plus: ^9.0.0
file_picker: ^5.2.10 file_picker: ^6.0.0
animations: ^2.0.4 animations: ^2.0.4
android_package_installer: android_package_installer:
git: git:
@ -64,7 +64,7 @@ dependencies:
flutter_markdown: ^0.6.14 flutter_markdown: ^0.6.14
flutter_archive: ^5.0.0 flutter_archive: ^5.0.0
hsluv: ^1.1.3 hsluv: ^1.1.3
connectivity_plus: ^4.0.2 connectivity_plus: ^5.0.0
shared_storage: ^0.8.0 shared_storage: ^0.8.0
dev_dependencies: dev_dependencies: