Compare commits

...

264 Commits

Author SHA1 Message Date
13aed69f0e Merge pull request #1063 from ImranR98/dev
Various bugfixes and minor enhancements (#454, #1026, #1050, #1052, #1060)
2023-11-03 19:44:28 -04:00
b03675811c Added new language menu entries 2023-11-03 19:40:19 -04:00
e4beac185e Merge remote-tracking branch 'origin/main' into dev 2023-11-03 19:38:23 -04:00
bdc7d9dd84 Merge pull request #1058 from Bardesss/main
Add nl.json
2023-11-03 19:38:01 -04:00
b49d1e0529 Merge pull request #998 from Mr-Bajs/main
Adding swedish translation
2023-11-03 19:37:55 -04:00
3c0eb48d53 Merge pull request #1027 from unbranched/patch-3
Update it.json
2023-11-03 19:37:36 -04:00
0d65531ec1 Merge pull request #1028 from Daviteusz/weblate-obtainium-translate
locale(pl): Update Polish translation
2023-11-03 19:37:31 -04:00
3848f9822b Merge pull request #1036 from iDazai/main
Update de.json
2023-11-03 19:37:24 -04:00
e1cdc002b9 Merge pull request #1043 from gidano/main
Update hu.json
2023-11-03 19:37:15 -04:00
168c1cf1ce Upgrade packages, increment version 2023-11-03 19:36:39 -04:00
5b142b4401 Various fixes and improvements (#454, #1026, #1050, #1051, #1052, #1060) 2023-11-03 19:35:42 -04:00
9be3d76590 Add nl.json
Adding Dutch translation to Obtanium
2023-11-01 10:01:44 +00:00
26971aa109 Upgrade packages 2023-10-25 18:22:58 -04:00
18f9049bc6 locale(pl): Update Polish translations 2023-10-24 11:51:19 +02:00
53cf253b67 Update hu.json 2023-10-23 10:20:51 +02:00
6d416f45a9 Higher versionCode after revert 2023-10-22 12:51:30 -04:00
a34a447164 Revert "Switched to WorkManager for reliability (#608)"
This reverts commit d3247a9ec1.
2023-10-22 12:50:54 -04:00
d3247a9ec1 Switched to WorkManager for reliability (#608) 2023-10-22 00:59:51 -04:00
dfb5a2b97f Update de.json
translated latest addition
2023-10-21 17:19:20 +02:00
deae96e191 Update it.json
Missed one string & fixed one
2023-10-19 08:56:18 +00:00
b8c0e18bb4 Merge pull request #1024 from ImranR98/dev
Don't replace / in versions (#1023), Fix HTML version extraction (#1021), Update check only for installed apps (#1012)
2023-10-18 19:47:34 -04:00
a083da1546 Upgrade packages, increment version 2023-10-18 19:46:11 -04:00
2518c70ec8 Bugfixes
Don't replace / in versions (#1023)
Fix HTML version extraction (#1021)
Update check only for installed apps (#1012)
2023-10-18 19:42:15 -04:00
6ead8d261c Merge pull request #1011 from LucasTavaresA/main
Update pt.json
2023-10-18 19:12:46 -04:00
b5f76a6ebc Merge pull request #1014 from DwainZwerg/patch-4
Update de.json (small improvements)
2023-10-18 19:12:40 -04:00
5e38bca535 Merge pull request #1015 from unbranched/patch-2
Update it.json
2023-10-18 19:12:34 -04:00
c39142ee77 Merge pull request #1016 from k03mad/ru_locale
Update ru translation
2023-10-18 19:12:23 -04:00
d0174479d7 update ru translation 2023-10-17 17:50:10 +03:00
aa23267d70 Update it.json 2023-10-17 09:58:12 +00:00
92f5da306d Update de.json (small improvements 2023-10-17 09:10:18 +00:00
8601e2af7a Update sv.json 2023-10-16 14:22:38 +00:00
5d9f31b99e Update sv.json 2023-10-16 14:21:50 +00:00
8b29158d8b Update sv.json 2023-10-16 14:17:13 +00:00
b68cf3c671 Update sv.json 2023-10-16 14:12:09 +00:00
858a50a414 Update pt.json 2023-10-15 20:13:45 -03:00
598df624df Update sv.json 2023-10-14 19:31:08 +00:00
e6da2b73ed Update sv.json 2023-10-14 19:16:26 +00:00
f44424d37a Update sv.json 2023-10-14 19:11:25 +00:00
623804ae68 Update sv.json 2023-10-14 18:35:37 +00:00
b7544939c2 Merge pull request #1008 from ImranR98/dev
Fix broken search (#1006)
2023-10-14 13:27:01 -04:00
63fa6d4660 Increment version 2023-10-14 13:25:49 -04:00
9506c11951 Fix broken search (#1006) 2023-10-14 13:25:26 -04:00
ce5639bb98 Update sv.json 2023-10-14 12:48:03 +00:00
99ef6ca861 Update sv.json 2023-10-14 12:32:28 +00:00
43d5a3da27 Update sv.json
updated
2023-10-14 12:30:55 +00:00
30723110af Merge branch 'ImranR98:main' into main 2023-10-14 12:07:11 +00:00
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
f76ea9bba5 Merge branch 'ImranR98:main' into main 2023-10-13 12:52:23 +00: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
6c30bd36d1 Create sv.json 2023-09-29 14:54:39 +00: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
845eaab73e Merge pull request #909 from ImranR98/dev
Bugfix: HTML Source was broken (could not add apps)
2023-09-16 15:27:11 -04:00
298ff190ec Bugfix: HTML Source was broken (could not add apps) 2023-09-16 15:26:35 -04:00
734a548bc3 Merge pull request #906 from ImranR98/dev
Increment version
2023-09-16 09:28:57 -04:00
591973d97f Increment version 2023-09-16 09:28:45 -04:00
a2f57ecd66 Merge pull request #903 from Daviteusz/weblate-obtainium-translate
locale(pl): Update Polish translation
2023-09-16 09:26:34 -04:00
13dcfb479d Merge pull request #902 from gidano/main
Update hu.json
2023-09-16 09:26:28 -04:00
3328d80130 Merge pull request #905 from saymanrifat/saymanrifat-721
Add back button to app detail page (#721)
2023-09-16 09:26:03 -04:00
f9df3ac0e2 Reduce top padding as we no longer need it 2023-09-16 09:25:26 -04:00
61d66e80d8 Add back button to app detail page (#721) 2023-09-16 18:31:57 +06:00
93e0360116 locale(pl): Update Polish translations 2023-09-16 12:45:50 +02:00
2cec5f7934 Update hu.json 2023-09-16 11:28:36 +02:00
8d4a09b007 Merge pull request #894 from markus-gitdev/main
Update de.json
2023-09-16 04:39:06 -04:00
6d8d2fa99d Merge branch 'main' into main 2023-09-16 04:38:48 -04:00
59ecd55530 Merge pull request #895 from DwainZwerg/patch-1
edit de.json
2023-09-16 04:38:05 -04:00
77fcc89e37 Merge branch 'main' into patch-1 2023-09-16 04:35:12 -04:00
7d63da4d9c Merge pull request #893 from bluefly000/japanese-translation
Update ja.json
2023-09-16 04:32:42 -04:00
8eea0bae99 Merge pull request #892 from iDazai/main
Update de.json
2023-09-16 04:32:35 -04:00
f5dcd8f776 Merge pull request #901 from ImranR98/dev
Fix background update issue (#896)
2023-09-16 04:31:29 -04:00
e1f2baeeda Fix background update issue (#896) 2023-09-16 04:31:03 -04:00
84d8ff8b0b Small improvement of the previous translation (especially "" instead of the English ''; my translations of the new features to be translated are mostly based on iDazai (https://github.com/ImranR98/Obtainium/pull/892/files) and markus-gitdev (https://github.com/ImranR98/Obtainium/pull/894/files)) 2023-09-14 08:46:51 +00:00
9862c22fdb Update de.json 2023-09-14 09:27:36 +02:00
8c82f53dd2 Update ja.json 2023-09-14 15:44:27 +09:00
5cf4c21ce3 Update de.json
Translated latest strings
2023-09-14 08:37:46 +02:00
4951f62d4a Merge pull request #890 from ImranR98/dev
- Fix export encoding (#887)
- Fix repeating background install for some apps (#886)
- More version filtering options for F-Droid (#885)
- Trim user URL input
2023-09-13 22:20:49 -04:00
4c0c4b7010 Trim user URL input 2023-09-13 22:19:09 -04:00
96051e614c Merge remote-tracking branch 'origin/main' into dev 2023-09-13 22:17:11 -04:00
a2e494b2ba More version filtering options for F-Droid (#885) 2023-09-13 22:15:10 -04:00
2d5a9bec84 Increment version, update packages 2023-09-13 21:27:18 -04:00
7d9571cfdd Fix repeating background install for some apps (#886) 2023-09-13 21:24:25 -04:00
acc6a780fa Fix export encoding (#887) 2023-09-13 21:01:39 -04:00
0e36d42a06 Merge pull request #863 from Daviteusz/weblate-obtainium-translate
locale(pl): Update Polish translation
2023-09-13 19:22:57 -04:00
5cfddd807a locale(pl): Update Polish translations 2023-09-11 22:05:32 +02:00
17b5604f2a Merge pull request #882 from ImranR98/dev
Bugfix
2023-09-10 23:29:38 -04:00
fdb6eed6d0 Slight tweak 2023-09-10 23:28:37 -04:00
13de0437b8 Bugfix 2023-09-10 23:23:35 -04:00
a43c45f310 Merge pull request #881 from ImranR98/dev
Pick export dir + auto-export (#283, #600) + UI fix
2023-09-10 23:15:36 -04:00
9c56a4d1fc Bugs 2023-09-10 23:15:16 -04:00
2aea1d2631 Bugfixes 2023-09-10 23:11:06 -04:00
118e05a0fa Bugfix 2023-09-10 22:47:04 -04:00
05f497787e Build script tweak 2023-09-10 22:37:32 -04:00
53cf4d0234 Pick export dir + auto-export (#283, #600) 2023-09-10 22:35:28 -04:00
6e735b1763 Enable auto-export on update checks 2023-09-10 22:24:18 -04:00
873a1a0683 Allow for alternative app dirs (unfinished) 2023-09-10 15:55:34 -04:00
27b1149d1c Fix padding when touch targets not highlighted 2023-09-10 14:16:19 -04:00
c1e64f111e Merge pull request #878 from ImranR98/dev
Fix Codeberg not finding APKs (#877), Make touch highlight visible in dark theme (#860)
2023-09-10 14:03:48 -04:00
b2af8448fd Increment version 2023-09-10 14:02:26 -04:00
8f44338e76 Make touch highlight visible in dark theme (#860) 2023-09-10 13:56:39 -04:00
e4a55abcb3 Fix Codeberg not finding APKs (#877) 2023-09-10 13:49:04 -04:00
d7348b4973 Merge pull request #871 from ImranR98/dev
Make less obvious target highlighting optional
2023-09-09 07:30:31 -04:00
09421230f2 Make less obvious target highlighting optional 2023-09-09 07:29:35 -04:00
4596e32258 Merge pull request #870 from ImranR98/dev
- Add Support for Private GitHub Repos (#857)
- Add Uptodown Source (#853)
- Add User-Defined Version Extraction for HTML Source (#861)
- Make release notes tap target more obvious (#860)
- Fix Missing Download Notification on App Add (#858)
- Prevent Access Token from Showing in Logs (#714)
2023-09-09 06:54:12 -04:00
4dc007a4f6 Update packages, increment version 2023-09-09 06:50:13 -04:00
c53a156969 Slight UI tweak 2023-09-09 06:48:55 -04:00
94bd0774fb Make release notes tap target more obvious (#860) 2023-09-09 06:00:36 -04:00
b178b1d780 Bugfix 2023-09-09 05:46:56 -04:00
cbc840378c Add Uptodown (#853) 2023-09-09 05:31:04 -04:00
aa7989c16d Custom version extraction for HTML (#861) 2023-09-09 04:41:44 -04:00
85f9336804 Re-add dl notification when adding app (#858) 2023-09-09 03:58:54 -04:00
d66be3ecda Private GitHub repos now work (#857) 2023-09-09 03:55:52 -04:00
c08e05bd6c Trying to use header-based HTTP auth (not working) 2023-09-06 21:30:45 -04:00
e08ab89fd4 Update issue templates 2023-09-06 18:33:04 -04:00
8ba0a0a776 Merge pull request #852 from ImranR98/dev
Fix Aptoide App ID inferral error (#851)
2023-09-04 03:57:56 -04:00
73ed0cea88 Increment version 2023-09-04 03:56:41 -04:00
58a378d212 Fix Aptoide App ID inferral error (#851) 2023-09-04 03:55:13 -04:00
553307ba70 Merge pull request #850 from ImranR98/dev
Auto Select Highest VersionCode APK (F-Droid) (#846)
2023-09-03 16:12:24 -04:00
78f73a9049 Auto Select Highest VersionCode APK (F-Droid) (#846) 2023-09-03 16:11:17 -04:00
abc69e7a0e Merge pull request #849 from ImranR98/dev
Bugfix in HTML Source (#848)
2023-09-03 15:37:43 -04:00
503914dbce Bugfix in HTML Source (#848) 2023-09-03 15:37:26 -04:00
47 changed files with 3669 additions and 1526 deletions

View File

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

View File

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

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

@ -2,31 +2,36 @@
Get Android App Updates Directly From the Source.
Obtainium allows you to install and update Open-Source Apps directly from their releases pages, and receive notifications when new releases are made available.
Obtainium allows you to install and update Apps directly from their releases pages, and receive notifications when new releases are made available.
Motivation: [Side Of Burritos - You should use this instead of F-Droid | How to use app RSS feed](https://youtu.be/FFz57zNR_M0)
Currently supported App sources:
- [GitHub](https://github.com/)
- [GitLab](https://gitlab.com/)
- [Codeberg](https://codeberg.org/)
- [F-Droid](https://f-droid.org/)
- [IzzyOnDroid](https://android.izzysoft.de/)
- [Mullvad](https://mullvad.net/en/)
- [Signal](https://signal.org/)
- [SourceForge](https://sourceforge.net/)
- [SourceHut](https://git.sr.ht/)
- [Aptoide](https://aptoide.com/)
- [APKMirror](https://apkmirror.com/) (Track-Only)
- [APKPure](https://apkpure.com/)
- [Huawei AppGallery](https://appgallery.huawei.com/)
- Third Party F-Droid Repos
- Jenkins Jobs
- [Steam](https://store.steampowered.com/mobile)
- [Telegram App](https://telegram.org)
- [Neutron Code](https://neutroncode.com)
- "HTML" (Fallback)
- Any other URL that returns an HTML page with links to APK files (if multiple, the last file alphabetically is picked)
- Open Source - General:
- [GitHub](https://github.com/)
- [GitLab](https://gitlab.com/)
- [Codeberg](https://codeberg.org/)
- [F-Droid](https://f-droid.org/)
- Third Party F-Droid Repos
- [IzzyOnDroid](https://android.izzysoft.de/)
- [SourceForge](https://sourceforge.net/)
- [SourceHut](https://git.sr.ht/)
- Other - General:
- [APKPure](https://apkpure.com/)
- [Aptoide](https://aptoide.com/)
- [Uptodown](https://uptodown.com/)
- [APKMirror](https://apkmirror.com/) (Track-Only)
- [Huawei AppGallery](https://appgallery.huawei.com/)
- Jenkins Jobs
- Open Source - App-Specific:
- [Mullvad](https://mullvad.net/en/)
- [Signal](https://signal.org/)
- [VLC](https://videolan.org/)
- Other - App-Specific:
- [Telegram App](https://telegram.org)
- [Steam Mobile Apps](https://store.steampowered.com/mobile)
- [Neutron Code](https://neutroncode.com)
- "HTML" (Fallback): Any other URL that returns an HTML page with links to APK files
## Installation

View File

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

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

@ -0,0 +1,330 @@
{
"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",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"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

@ -12,8 +12,6 @@
"ok": "Okay",
"and": "und",
"githubPATLabel": "GitHub Personal Access Token (Erhöht das Ratenlimit)",
"githubPATHint": "PAT muss in diesem Format sein: Benutzername:Token",
"githubPATFormat": "Benutzername:Token",
"includePrereleases": "Vorabversionen einbeziehen",
"fallbackToOlderReleases": "Fallback auf ältere Versionen",
"filterReleaseTitlesByRegEx": "Release-Titel nach regulärem Ausdruck\nfiltern",
@ -30,8 +28,8 @@
"xIsTrackOnly": "{} ist nur zur Nachverfolgung",
"source": "Quelle",
"app": "App",
"appsFromSourceAreTrackOnly": "Apps aus dieser Quelle sind 'Nur Nachverfolgen'.",
"youPickedTrackOnly": "Sie haben die Option 'Nur Nachverfolgen' gewählt.",
"appsFromSourceAreTrackOnly": "Apps aus dieser Quelle sind nur zum Nachverfolgen.",
"youPickedTrackOnly": "Sie haben die Option Nur Nachverfolgen gewählt.",
"trackOnlyAppDescription": "Die App wird auf Updates überwacht, aber Obtainium wird sie nicht herunterladen oder installieren.",
"cancelled": "Abgebrochen",
"appAlreadyAdded": "App bereits hinzugefügt",
@ -40,10 +38,10 @@
"appSourceURL": "Quell-URL der App",
"error": "Fehler",
"add": "Hinzufügen",
"searchSomeSourcesLabel": "Suche (nur bestimmte Quellen)",
"searchSomeSourcesLabel": "Suche (nur für bestimmte Quellen)",
"search": "Suchen",
"additionalOptsFor": "Zusatzoptionen für {}",
"supportedSourcesBelow": "Unterstützte Quellen:",
"supportedSources": "Unterstützte Quellen",
"trackOnlyInBrackets": "(Nur Nachverfolgen)",
"searchableInBrackets": "(Durchsuchbar)",
"appsString": "Apps",
@ -72,7 +70,7 @@
"yes": "Ja",
"markSelectedAppsUpdated": "Markiere ausgewählte Apps als aktuell",
"pinToTop": "Oben anheften",
"unpinFromTop": "'Oben anheften' aufheben",
"unpinFromTop": "Oben anheften aufheben",
"resetInstallStatusForSelectedAppsQuestion": "Installationsstatus für ausgewählte Apps zurücksetzen?",
"installStatusOfXWillBeResetExplanation": "Der Installationsstatus der ausgewählten Apps wird zurückgesetzt. Dies kann hilfreich sein, wenn die in Obtainium angezeigte App-Version aufgrund fehlgeschlagener Aktualisierungen oder anderer Probleme falsch ist.",
"shareSelectedAppURLs": "Ausgewählte App-URLs teilen",
@ -124,15 +122,15 @@
"ascending": "Aufsteigend",
"descending": "Absteigend",
"bgUpdateCheckInterval": "Prüfintervall für Hintergrundaktualisierung",
"neverManualOnly": "Nie - nur manuell",
"neverManualOnly": "Nie nur manuell",
"appearance": "Aussehen",
"showWebInAppView": "Quellwebseite in der App-Ansicht anzeigen",
"pinUpdates": "Apps mit Aktualisierungen oben anheften",
"updates": "Aktualisierungen",
"sourceSpecific": "Quellenspezifisch",
"appSource": "App-Quelle",
"noLogs": "Keine Protokolle",
"appLogs": "App Protokolle",
"noLogs": "Keine Logs",
"appLogs": "App-Logs",
"close": "Schließen",
"share": "Teilen",
"appNotFound": "App nicht gefunden",
@ -172,12 +170,12 @@
"lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}",
"remove": "Entfernen",
"yesMarkUpdated": "Ja, als aktualisiert markieren",
"fdroid": "F-Droid Official",
"fdroid": "offizielles F-Droid-Repo",
"appIdOrName": "App ID oder Name",
"appId": "App ID",
"appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden",
"reposHaveMultipleApps": "Repos können mehrere Apps enthalten",
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
"fdroidThirdPartyRepo": "F-Droid Drittparteienrepo",
"steam": "Steam",
"steamMobile": "Steam Mobile",
"steamChat": "Steam Chat",
@ -244,26 +242,54 @@
"appsPossiblyUpdated": "App Aktualisierungen wurden versucht",
"appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Updates für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden",
"xWasPossiblyUpdatedToY": "{} wurde möglicherweise aktualisiert auf {}.",
"enableBackgroundUpdates": "Aktiviere Hintergrundaktualisierungen",
"backgroundUpdateReqsExplanation": "Die Hintergrundaktualisierung ist möglicherweise nicht für alle Apps möglich.",
"backgroundUpdateLimitsExplanation": "Der Erfolg einer Hintergrundinstallation kann nur festgestellt werden, wenn Obtainium geöffnet wird.",
"verifyLatestTag": "Überprüfe das 'latest' Tag",
"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)",
"bgUpdatesOnWiFiOnly": "Hintergrundaktualisierungen deaktivieren, wenn kein WLAN vorhanden ist",
"autoSelectHighestVersionCode": "Automatisch höchste APK-Version auswählen",
"versionExtractionRegEx": "Versions-Extraktion per RegEx",
"matchGroupToUse": "zu verwendende Gruppe abgleichen",
"highlightTouchTargets": "Weniger offensichtliche Touch-Ziele hervorheben",
"pickExportDir": "Export-Verzeichnis wählen",
"autoExportOnChanges": "Automatischer Export bei Änderung(en)",
"filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern",
"trySelectingSuggestedVersionCode": "Versuchen, den vorgeschlagenen APK-Versionscode 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",
"onlyCheckInstalledOrTrackOnlyApps": "Überprüfe nur installierte und mit „nur Nachverfolgen“ markierte Apps nach Aktualisierungen",
"removeAppQuestion": {
"one": "App entfernen?",
"other": "Apps entfernen?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut",
"other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten 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"
},
"bgUpdateGotErrorRetryInMinutes": {
"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"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "Hintergrundaktualisierungsprüfung fand {} Aktualisierung - benachrichtigt den Benutzer, falls erforderlich",
"other": "Hintergrundaktualisierungsprüfung fand {} Aktualisierungen - benachrichtigt den Benutzer, falls erforderlich"
"one": "Die Hintergrundaktualisierungsprüfung fand {} Aktualisierung benachrichtigt den Benutzer, falls erforderlich",
"other": "Die Hintergrundaktualisierungsprüfung fand {} Aktualisierungen benachrichtigt den Benutzer, falls erforderlich"
},
"apps": {
"one": "{} App",
@ -286,8 +312,8 @@
"other": "{} Tage"
},
"clearedNLogsBeforeXAfterY": {
"one": "{n} Protokoll gelöscht (vorher = {vorher}, nachher = {nachher})",
"other": "{n} Protokolle gelöscht (vorher = {vorher}, nachher = {nachher})"
"one": "{n} Log gelöscht (vorher = {before}, nachher = {after})",
"other": "{n} Logs gelöscht (vorher = {before}, nachher = {after})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} und 1 weitere App haben Aktualisierungen.",

View File

@ -12,8 +12,6 @@
"ok": "Okay",
"and": "and",
"githubPATLabel": "GitHub Personal Access Token (Increases Rate Limit)",
"githubPATHint": "PAT must be in this format: username:token",
"githubPATFormat": "username:token",
"includePrereleases": "Include prereleases",
"fallbackToOlderReleases": "Fallback to older releases",
"filterReleaseTitlesByRegEx": "Filter Release Titles by Regular Expression",
@ -43,7 +41,7 @@
"searchSomeSourcesLabel": "Search (Some Sources Only)",
"search": "Search",
"additionalOptsFor": "Additional Options for {}",
"supportedSourcesBelow": "Supported Sources:",
"supportedSources": "Supported Sources",
"trackOnlyInBrackets": "(Track-Only)",
"searchableInBrackets": "(Searchable)",
"appsString": "Apps",
@ -252,6 +250,31 @@
"intermediateLinkNotFound": "Intermediate link not found",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"versionExtractionRegEx": "Version Extraction RegEx",
"matchGroupToUse": "Match Group to Use for Version Extraction Regex",
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes",
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
"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",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"removeAppQuestion": {
"one": "Remove App?",
"other": "Remove Apps?"
@ -304,4 +327,4 @@
"one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated."
}
}
}

View File

@ -12,8 +12,6 @@
"ok": "Correcto",
"and": "y",
"githubPATLabel": "Token de Acceso Personal de GitHub (Reduce tiempos de espera)",
"githubPATHint": "El TAP debe tener este formato: nombre_de_usuario:token",
"githubPATFormat": "nombre_de_usuario:token",
"includePrereleases": "Incluir versiones preliminares",
"fallbackToOlderReleases": "Retorceder a versiones previas",
"filterReleaseTitlesByRegEx": "Filtra Títulos de Versiones mediantes Expresiones Regulares",
@ -43,7 +41,7 @@
"searchSomeSourcesLabel": "Buscar (Solo Algunas Fuentes)",
"search": "Buscar",
"additionalOptsFor": "Opciones Adicionales para {}",
"supportedSourcesBelow": "Fuentes Soportadas:",
"supportedSources": "Fuentes Soportadas",
"trackOnlyInBrackets": "(Solo Seguimiento)",
"searchableInBrackets": "(Soporta Búsquedas)",
"appsString": "Aplicaciones",
@ -244,11 +242,39 @@
"appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"enableBackgroundUpdates": "Enable background updates",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
"intermediateLinkNotFound": "Intermediate link not found",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"versionExtractionRegEx": "Version Extraction RegEx",
"matchGroupToUse": "Match Group to Use",
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes",
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
"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",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"removeAppQuestion": {
"one": "¿Eliminar Aplicación?",
"other": "¿Eliminar Aplicaciones?"
@ -301,4 +327,4 @@
"one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated."
}
}
}

View File

@ -12,8 +12,6 @@
"ok": "باشه",
"and": "و",
"githubPATLabel": "توکن دسترسی شخصی گیت هاب(محدودیت نرخ را افزایش میدهد)",
"githubPATHint": "PAT باید در این قالب باشد: username:token",
"githubPATFormat": "username:token",
"includePrereleases": "شامل نسخه های اولیه",
"fallbackToOlderReleases": "بازگشت به نسخه های قدیمی تر",
"filterReleaseTitlesByRegEx": "عناوین انتشار را با بیان منظم فیلتر کنید",
@ -43,7 +41,7 @@
"searchSomeSourcesLabel": "جستجو (فقط برخی منابع)",
"search": "جستجو کردن",
"additionalOptsFor": "گزینه های اضافی برای {}",
"supportedSourcesBelow": "منابع پشتیبانی شده:",
"supportedSources": "منابع پشتیبانی شده",
"trackOnlyInBrackets": "«فقط ردیابی»",
"searchableInBrackets": "(قابل جستجو)",
"appsString": "برنامه ها",
@ -244,11 +242,39 @@
"appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"enableBackgroundUpdates": "Enable background updates",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
"intermediateLinkNotFound": "Intermediate link not found",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"versionExtractionRegEx": "Version Extraction RegEx",
"matchGroupToUse": "Match Group to Use",
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes",
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
"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": "بررسی به‌روزرسانی‌ها",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"removeAppQuestion": {
"one": "برنامه حذف شود؟",
"other": "برنامه ها حذف شوند؟"
@ -301,4 +327,4 @@
"one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated."
}
}
}

View File

@ -12,8 +12,6 @@
"ok": "Okay",
"and": "et",
"githubPATLabel": "Jeton d'Accès Personnel GitHub (Augmente la limite de débit)",
"githubPATHint": "Le JAP doit être dans ce format : username:token",
"githubPATFormat": "username:token",
"includePrereleases": "Inclure les avant-premières",
"fallbackToOlderReleases": "Retour aux anciennes versions",
"filterReleaseTitlesByRegEx": "Filtrer les titres de version par expression régulière",
@ -43,7 +41,7 @@
"searchSomeSourcesLabel": "Rechercher (certaines sources uniquement)",
"search": "Rechercher",
"additionalOptsFor": "Options supplémentaires pour {}",
"supportedSourcesBelow": "Sources prises en charge :",
"supportedSources": "Sources prises en charge ",
"trackOnlyInBrackets": "(Suivi uniquement)",
"searchableInBrackets": "(Recherchable)",
"appsString": "Applications",
@ -244,11 +242,39 @@
"appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"enableBackgroundUpdates": "Enable background updates",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
"intermediateLinkNotFound": "Intermediate link not found",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"versionExtractionRegEx": "Version Extraction RegEx",
"matchGroupToUse": "Match Group to Use",
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes",
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
"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",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"removeAppQuestion": {
"one": "Supprimer l'application ?",
"other": "Supprimer les applications ?"
@ -301,4 +327,4 @@
"one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated."
}
}
}

View File

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

@ -11,9 +11,7 @@
"unexpectedError": "Errore imprevisto",
"ok": "Va bene",
"and": "e",
"githubPATLabel": "GitHub Personal Access Token (diminuisce limite di traffico)",
"githubPATHint": "PAT deve seguire questo formato: nomeutente:token",
"githubPATFormat": "nomeutente:token",
"githubPATLabel": "GitHub Personal Access Token (aumenta limite di traffico)",
"includePrereleases": "Includi prerelease",
"fallbackToOlderReleases": "Ripiega su release precedenti",
"filterReleaseTitlesByRegEx": "Filtra release con espressioni regolari",
@ -43,7 +41,7 @@
"searchSomeSourcesLabel": "Cerca (solo per alcune fonti)",
"search": "Cerca",
"additionalOptsFor": "Opzioni aggiuntive per {}",
"supportedSourcesBelow": "Fonti supportate:",
"supportedSources": "Fonti supportate",
"trackOnlyInBrackets": "(Solo-Monitoraggio)",
"searchableInBrackets": "(ricercabile)",
"appsString": "App",
@ -53,9 +51,9 @@
"percentProgress": "Avanzamento: {}%",
"pleaseWait": "In attesa",
"updateAvailable": "Aggiornamento disponibile",
"estimateInBracketsShort": "(prev.)",
"estimateInBracketsShort": "(stim.)",
"notInstalled": "Non installato",
"estimateInBrackets": "(previsto)",
"estimateInBrackets": "(stimato)",
"selectAll": "Seleziona tutto",
"deselectN": "Deseleziona {}",
"xWillBeRemovedButRemainInstalled": "Verà effettuata la rimozione di {}, ma non la disinstallazione.",
@ -63,7 +61,7 @@
"removeSelectedApps": "Rimuovi le app selezionate",
"updateX": "Aggiorna {}",
"installX": "Installa {}",
"markXTrackOnlyAsUpdated": "Contrassegna {}\n(Solo-Monitoraggio)\ncome aggiornato",
"markXTrackOnlyAsUpdated": "Contrassegna {}\n(Solo-Monitoraggio)\ncome aggiornata",
"changeX": "Modifica {}",
"installUpdateApps": "Installa/Aggiorna app",
"installUpdateSelectedApps": "Installa/Aggiorna le app selezionate",
@ -130,7 +128,7 @@
"pinUpdates": "Fissa aggiornamenti disponibili in alto",
"updates": "Aggiornamenti",
"sourceSpecific": "Specifiche per la fonte",
"appSource": "Sorgente dell'app",
"appSource": "Codice dell'app",
"noLogs": "Nessun log",
"appLogs": "Log dell'app",
"close": "Chiudi",
@ -171,7 +169,7 @@
"installedVersionX": "Versione installata: {}",
"lastUpdateCheckX": "Ultimo controllo degli aggiornamenti: {}",
"remove": "Rimuovi",
"yesMarkUpdated": "Sì, contrassegna come aggiornato",
"yesMarkUpdated": "Sì, contrassegna come aggiornata",
"fdroid": "F-Droid ufficiale",
"appIdOrName": "ID o nome dell'app",
"appId": "ID dell'app",
@ -182,9 +180,9 @@
"steamMobile": "Steam Mobile",
"steamChat": "Steam Chat",
"install": "Installa",
"markInstalled": "Contrassegna come installato",
"markInstalled": "Contrassegna come installata",
"update": "Aggiorna",
"markUpdated": "Contrassegna come aggiornato",
"markUpdated": "Contrassegna come aggiornata",
"additionalOptions": "Opzioni aggiuntive",
"disableVersionDetection": "Disattiva il rilevamento della versione",
"noVersionDetectionExplanation": "Questa opzione dovrebbe essere usata solo per le app la cui versione non viene rilevata correttamente.",
@ -223,32 +221,60 @@
"dontShowTrackOnlyWarnings": "Non mostrare gli avvisi 'Solo-Monitoraggio'",
"dontShowAPKOriginWarnings": "Non mostrare gli avvisi di origine dell'APK",
"moveNonInstalledAppsToBottom": "Sposta le app non installate in fondo alla lista",
"gitlabPATLabel": "GitLab Personal Access Token\n(attiva la ricerca and Better APK Discovery)",
"gitlabPATLabel": "GitLab Personal Access Token\n(attiva la ricerca e migliora la rilevazione di apk)",
"about": "Informazioni",
"requiresCredentialsInSettings": "Servono credenziali aggiuntive (in Impostazioni)",
"checkOnStart": "Controlla una volta all'avvio",
"tryInferAppIdFromCode": "Prova a dedurre l'ID dell'app dal codice sorgente",
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
"pickHighestVersionCode": "Auto-select highest version code APK",
"checkUpdateOnDetailPage": "Check for updates on opening an App detail page",
"disablePageTransitions": "Disable page transition animations",
"reversePageTransitions": "Reverse page transition animations",
"minStarCount": "Minimum Star Count",
"addInfoBelow": "Add this info below.",
"addInfoInSettings": "Add this info in the Settings.",
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
"appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"removeOnExternalUninstall": "Rimuovi automaticamente app disinstallate esternamente",
"pickHighestVersionCode": "Auto-seleziona APK con version code più alto",
"checkUpdateOnDetailPage": "Controlla aggiornamenti all'apertura dei dettagli dell'app",
"disablePageTransitions": "Disattiva animazioni di transizione pagina",
"reversePageTransitions": "Inverti animazioni di transizione pagina",
"minStarCount": "Numero minimo di stelle",
"addInfoBelow": "Aggiungi questa info sotto.",
"addInfoInSettings": "Aggiungi questa info nelle impostazioni.",
"githubSourceNote": "Il limite di ricerca GitHub può essere evitato usando una chiave API.",
"gitlabSourceNote": "L'estrazione di APK da GitLab potrebbe non funzionare senza chiave API.",
"sortByFileNamesNotLinks": "Ordina per nome del file invece dei link completi",
"filterReleaseNotesByRegEx": "Filtra le note di rilascio con espressione regolare",
"customLinkFilterRegex": "Filtra link APK personalizzato con espressione regolare (predefinito '.apk$')",
"appsPossiblyUpdated": "Aggiornamenti app tentati",
"appsPossiblyUpdatedNotifDescription": "Notifica all'utente che sono stati potenzialmente applicati in secondo piano aggiornamenti a una o più app",
"xWasPossiblyUpdatedToY": "{} potrebbe essere stata aggiornata alla {}.",
"enableBackgroundUpdates": "Attiva aggiornamenti in secondo piano",
"backgroundUpdateReqsExplanation": "Gli aggiornamenti in secondo piano potrebbero non essere possibili per tutte le app.",
"backgroundUpdateLimitsExplanation": "La riuscita di un'installazione in secondo piano può essere determinata solo quando viene aperto Obtainium.",
"verifyLatestTag": "Verifica l'etichetta 'Latest'",
"intermediateLinkRegex": "Filtra un link 'Intermedio' da visitare prima",
"intermediateLinkNotFound": "Link intermedio non trovato",
"exemptFromBackgroundUpdates": "Esente da aggiornamenti in secondo piano (se attivo)",
"bgUpdatesOnWiFiOnly": "Disattiva aggiornamenti in secondo piano quando non si usa il WiFi",
"autoSelectHighestVersionCode": "Auto-seleziona APK con versionCode più alto",
"versionExtractionRegEx": "RegEx di estrazione versione",
"matchGroupToUse": "Gruppo da usare",
"highlightTouchTargets": "Evidenzia elementi toccabili meno ovvi",
"pickExportDir": "Scegli cartella esp.",
"autoExportOnChanges": "Auto-esporta dopo modifiche",
"filterVersionsByRegEx": "Filtra versioni con espressione regolare",
"trySelectingSuggestedVersionCode": "Prova a selezionare APK con versionCode suggerito",
"dontSortReleasesList": "Conserva l'ordine di release da API",
"reverseSort": "Ordine inverso",
"debugMenu": "Menu di debug",
"bgTaskStarted": "Attività in secondo piano iniziata - controllo log.",
"runBgCheckNow": "Inizia aggiornamento in secondo piano ora",
"versionExtractWholePage": "Applica regex di estrazione versione a tutta la pagina",
"installing": "Installazione",
"skipUpdateNotifications": "Salta notifiche di aggiornamento",
"updatesAvailableNotifChannel": "Aggiornamenti disponibili",
"appsUpdatedNotifChannel": "App aggiornate",
"appsPossiblyUpdatedNotifChannel": "Aggiornamenti app tentati",
"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",
"onlyCheckInstalledOrTrackOnlyApps": "Cerca aggiornamenti solo per app installate e app in Solo-Monitoraggio",
"removeAppQuestion": {
"one": "Rimuovere l'app?",
"other": "Rimuovere le app?"
@ -298,7 +324,7 @@
"other": "{} e altre {} app sono state aggiornate."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated."
"one": "{} e un'altra app potrebbero essere state aggiornate.",
"other": "{} e altre {} app potrebbero essere state aggiornate."
}
}

View File

@ -12,8 +12,6 @@
"ok": "OK",
"and": "と",
"githubPATLabel": "GitHub パーソナルアクセストークン (レート制限の引き上げ)",
"githubPATHint": "PATは次の形式でなければなりません: ユーザー名:トークン",
"githubPATFormat": "ユーザー名:トークン",
"includePrereleases": "プレリリースを含む",
"fallbackToOlderReleases": "旧リリースへのフォールバック",
"filterReleaseTitlesByRegEx": "正規表現でリリースタイトルをフィルタリングする",
@ -43,7 +41,7 @@
"searchSomeSourcesLabel": "検索 (一部ソースのみ)",
"search": "検索",
"additionalOptsFor": "{}の追加オプション",
"supportedSourcesBelow": "対応するソース:",
"supportedSources": "対応するソース",
"trackOnlyInBrackets": "(追跡のみ)",
"searchableInBrackets": "(検索可能)",
"appsString": "アプリ",
@ -146,7 +144,7 @@
"updatesAvailable": "アップデートが利用可能",
"updatesAvailableNotifDescription": "Obtainiumが追跡している1つまたは複数のアプリのアップデートが利用可能であることをユーザーに通知する",
"noNewUpdates": "新しいアップデートはありません",
"xHasAnUpdate": "{} のアップデートが利用可能です",
"xHasAnUpdate": "{} のアップデートが利用可能です",
"appsUpdated": "アプリをアップデートしました",
"appsUpdatedNotifDescription": "1つまたは複数のAppのアップデートがバックグラウンドで適用されたことをユーザーに通知する",
"xWasUpdatedToY": "{} が {} にアップデートされました",
@ -243,13 +241,40 @@
"customLinkFilterRegex": "正規表現によるカスタムリンクフィルター (デフォルト '.apk$')",
"appsPossiblyUpdated": "アプリのアップデートを試行",
"appsPossiblyUpdatedNotifDescription": "1つまたは複数のアプリのアップデートがバックグラウンドで適用された可能性があることをユーザーに通知する",
"xWasPossiblyUpdatedToY": "{} が {} にアップデートされた可能性があります",
"xWasPossiblyUpdatedToY": "{} が {} にアップデートされた可能性があります",
"enableBackgroundUpdates": "バックグラウンドアップデートを有効化する",
"backgroundUpdateReqsExplanation": "バックグラウンドアップデートは、すべてのアプリで可能とは限りません。",
"backgroundUpdateLimitsExplanation": "バックグラウンドアップデートが成功したかどうかは、Obtainiumを起動したときにしか判断できません。",
"verifyLatestTag": "'latest'タグを確認する",
"intermediateLinkRegex": "最初にアクセスする「中間」リンクをフィルタリングする",
"intermediateLinkNotFound": "中間リンクが見つかりませんでした",
"exemptFromBackgroundUpdates": "バックグラウンドアップデートを行わない (有効な場合)",
"bgUpdatesOnWiFiOnly": "WiFiを使用していない場合バックグラウンドアップデートを無効にする",
"autoSelectHighestVersionCode": "最も高いバージョンコードのAPKを自動で選択する",
"versionExtractionRegEx": "バージョン抽出の正規表現",
"matchGroupToUse": "使用するマッチしたグループ",
"highlightTouchTargets": "目立たないタップ可能な対象をハイライトする",
"pickExportDir": "エクスポートディレクトリを選択",
"autoExportOnChanges": "変更があった際に自動でエクスポートする",
"filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする",
"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": "アップデートを確認中",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"removeAppQuestion": {
"one": "アプリを削除しますか?",
"other": "アプリを削除しますか?"
@ -291,15 +316,15 @@
"other": "{n} 個のログをクリアしました (前 = {before}, 後 = {after})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} とさらに {} 個のアプリのアップデートが利用可能です",
"other": "{} とさらに {} 個のアプリのアップデートが利用可能です"
"one": "{} とさらに {} 個のアプリのアップデートが利用可能です",
"other": "{} とさらに {} 個のアプリのアップデートが利用可能です"
},
"xAndNMoreUpdatesInstalled": {
"one": "{} とさらに {} 個のアプリがアップデートされました",
"other": "{} とさらに {} 個のアプリがアップデートされました"
"one": "{} とさらに {} 個のアプリがアップデートされました",
"other": "{} とさらに {} 個のアプリがアップデートされました"
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} とさらに 1 個のアプリがアップデートされた可能性があります",
"other": "{} とさらに {} 個のアプリがアップデートされた可能性があります"
"one": "{} とさらに 1 個のアプリがアップデートされた可能性があります",
"other": "{} とさらに {} 個のアプリがアップデートされた可能性があります"
}
}
}

330
assets/translations/nl.json Normal file
View File

@ -0,0 +1,330 @@
{
"invalidURLForSource": "Geen valide {} app URL",
"noReleaseFound": "Kan geen geschikte release vinden",
"noVersionFound": "Kan de versie niet bepalen",
"urlMatchesNoSource": "URL komt niet overeen met bekende bron",
"cantInstallOlderVersion": "Kan geen oudere versie van de app installeren",
"appIdMismatch": "Gedownloade pakket-ID komt niet overeen met de bestaande app-ID",
"functionNotImplemented": "Deze class heeft deze functie niet geïmplementeerd.",
"placeholder": "Plaatshouder",
"someErrors": "Er zijn enkele fouten opgetreden",
"unexpectedError": "Onverwachte fout",
"ok": "Ok",
"and": "en",
"githubPATLabel": "GitHub Personal Access Token (Verhoogt limiet aantal verzoeken)",
"includePrereleases": "Bevat prereleases",
"fallbackToOlderReleases": "Terugvallen op oudere releases",
"filterReleaseTitlesByRegEx": "Filter release-titels met reguliere expressies.",
"invalidRegEx": "Ongeldige reguliere expressie",
"noDescription": "Geen omschrijving",
"cancel": "Annuleer",
"continue": "Ga verder",
"requiredInBrackets": "(Verplicht)",
"dropdownNoOptsError": "FOUTMELDING: DROPDOWN MOET TENMINSTE ÉÉN OPT HEBBEN",
"colour": "Kleur",
"githubStarredRepos": "GitHub Starred Repos",
"uname": "Gebruikersnaam",
"wrongArgNum": "Onjuist aantal argumenten verstrekt.",
"xIsTrackOnly": "{} is Track-Only",
"source": "Bron",
"app": "App",
"appsFromSourceAreTrackOnly": "Apps van deze bron zijn 'Track-Only'.",
"youPickedTrackOnly": "Je hebt de 'Track-Only' optie geselecteerd.",
"trackOnlyAppDescription": "De app zal worden gevolgd voor updates, maar Obtainium zal niet in staat zijn om deze te downloaden of te installeren.",
"cancelled": "Geannuleerd",
"appAlreadyAdded": "App al toegevoegd",
"alreadyUpToDateQuestion": "Is de app al up-to-date?",
"addApp": "App toevoegen",
"appSourceURL": "App bron URL",
"error": "Foutmelding",
"add": "Toevoegen",
"searchSomeSourcesLabel": "Zoeken (Alleen sommige bronnen)",
"search": "Zoeken",
"additionalOptsFor": "Aanvullende opties voor {}",
"supportedSources": "Ondersteunde bronnen",
"trackOnlyInBrackets": "(Track-Only)",
"searchableInBrackets": "(Doorzoekbaar)",
"appsString": "Apps",
"noApps": "Geen Apps",
"noAppsForFilter": "Geen Apps voor filter",
"byX": "Door {}",
"percentProgress": "Vooruitgang: {}%",
"pleaseWait": "Even geduld",
"updateAvailable": "Update beschikbaar",
"estimateInBracketsShort": "(Ong.)",
"notInstalled": "Niet geinstalleerd",
"estimateInBrackets": "(Ongeveer)",
"selectAll": "Selecteer alles",
"deselectN": "Deselecteer {}",
"xWillBeRemovedButRemainInstalled": "{} zal worden verwijderd uit Obtainium, maar blijft geïnstalleerd op het apparaat.",
"removeSelectedAppsQuestion": "Geselecteerde apps verwijderen??",
"removeSelectedApps": "Geselecteerde apps verwijderen",
"updateX": "Update {}",
"installX": "Installeer {}",
"markXTrackOnlyAsUpdated": "Markeer {}\n(Track-Only)\nals up-to-date",
"changeX": "Verander {}",
"installUpdateApps": "Installeer/Update apps",
"installUpdateSelectedApps": "Installeer/Update geselecteerde apps",
"markXSelectedAppsAsUpdated": "{} geselecteerde apps markeren als up-to-date?",
"no": "Nee",
"yes": "Ja",
"markSelectedAppsUpdated": "Markeer geselecteerde aps als up-to-date",
"pinToTop": "Vastzetten aan de bovenkant",
"unpinFromTop": "Losmaken van de bovenkant",
"resetInstallStatusForSelectedAppsQuestion": "Installatiestatus resetten voor geselecteerde apps?",
"installStatusOfXWillBeResetExplanation": "De installatiestatus van alle geselecteerde apps zal worden gereset.\n\nDit kan helpen wanneer de versie van de app die in Obtainium wordt weergegeven onjuist is vanwege mislukte updates of andere problemen.",
"shareSelectedAppURLs": "Deel geselecteerde app URL's",
"resetInstallStatus": "Reset installatiestatus",
"more": "Meer",
"removeOutdatedFilter": "Verwijder out-of-date app filter",
"showOutdatedOnly": "Toon alleen out-of-date apps",
"filter": "Filter",
"filterActive": "Filter *",
"filterApps": "Filter apps",
"appName": "App naam",
"author": "Auteur",
"upToDateApps": "Up-to-date apps",
"nonInstalledApps": "Niet-geïnstalleerde apps",
"importExport": "Import/Export",
"settings": "Instellingen",
"exportedTo": "Geëxporteerd naar {}",
"obtainiumExport": "Obtainium export",
"invalidInput": "Ongeldige invoer",
"importedX": "Geïmporteerd {}",
"obtainiumImport": "Obtainium import",
"importFromURLList": "Importeer van URL-lijsten",
"searchQuery": "Zoekopdracht",
"appURLList": "App URL-lijst",
"line": "Lijn",
"searchX": "Zoek {}",
"noResults": "Geen resultaten gevonden",
"importX": "Import {}",
"importedAppsIdDisclaimer": "Geïmporteerde apps kunnen mogelijk onjuist worden weergegeven als \"Niet geïnstalleerd\".\nOm dit op te lossen, herinstalleer ze via Obtainium.\nDit zou geen invloed moeten hebben op app-gegevens.\n\nDit heeft alleen invloed op URL- en importmethoden van derden.",
"importErrors": "Import foutmeldingen",
"importedXOfYApps": "{} van {} apps geïmporteerd.",
"followingURLsHadErrors": "De volgende URL's bevatten fouten:",
"okay": "Ok",
"selectURL": "Selecteer URL",
"selectURLs": "Selecteer URL's",
"pick": "Kies",
"theme": "Thema",
"dark": "Donker",
"light": "Licht",
"followSystem": "Volg systeem",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "Gebruik zwart thema",
"appSortBy": "App sorteren op",
"authorName": "Auteur/Naam",
"nameAuthor": "Naam/Auteur",
"asAdded": "Zoals toegevoegd",
"appSortOrder": "App sorteervolgorde",
"ascending": "Oplopend",
"descending": "Aflopend",
"bgUpdateCheckInterval": "Frequentie voor achtergrondupdatecontrole",
"neverManualOnly": "Nooit - Alleen handmatig",
"appearance": "Weergave",
"showWebInAppView": "Toon de bronwebpagina in app-weergave",
"pinUpdates": "Updates bovenaan in de apps-weergave vastpinnen",
"updates": "Updates",
"sourceSpecific": "Bron-specifiek",
"appSource": "App bron",
"noLogs": "Geen logs",
"appLogs": "App logs",
"close": "Sluiten",
"share": "Delen",
"appNotFound": "App niet gevonden",
"obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "Kies een APK",
"appHasMoreThanOnePackage": "{} heeft meer dan één package:",
"deviceSupportsXArch": "Jouw apparaat support de {} CPU-architectuur.",
"deviceSupportsFollowingArchs": "Je apparaat ondersteunt de volgende CPU-architecturen:",
"warning": "Waarschuwing",
"sourceIsXButPackageFromYPrompt": "De appbron is '{}' maar de release package komt van '{}'. Doorgaan?",
"updatesAvailable": "Updates beschikbaar",
"updatesAvailableNotifDescription": "Stelt de gebruiker op de hoogte dat er updates beschikbaar zijn voor één of meer apps die worden bijgehouden door Obtainium.",
"noNewUpdates": "Geen nieuwe updates.",
"xHasAnUpdate": "{} heeft een update.",
"appsUpdated": "Apps bijgewerkt",
"appsUpdatedNotifDescription": "Stelt de gebruiker op de hoogte dat updates voor één of meer apps in de achtergrond zijn toegepast.",
"xWasUpdatedToY": "{} is bijgewerkt naar {}.",
"errorCheckingUpdates": "Fout bij het controleren op updates",
"errorCheckingUpdatesNotifDescription": "Een melding die verschijnt wanneer het controleren op updates in de achtergrond mislukt",
"appsRemoved": "Apps verwijderd",
"appsRemovedNotifDescription": "Stelt de gebruiker op de hoogte dat één of meer apps zijn verwijderd vanwege fouten tijdens het laden ervan",
"xWasRemovedDueToErrorY": "{} is verwijderd vanwege deze foutmelding: {}",
"completeAppInstallation": "Complete app installatie",
"obtainiumMustBeOpenToInstallApps": "Obtainium moet geopend zijn om apps te installeren",
"completeAppInstallationNotifDescription": "Vraagt de gebruiker om terug te keren naar Obtainium om de installatie van een app af te ronden",
"checkingForUpdates": "Controleren op updates",
"checkingForUpdatesNotifDescription": "Tijdelijke melding die verschijnt tijdens het controleren op updates",
"pleaseAllowInstallPerm": "Sta Obtainium toe om apps te installeren",
"trackOnly": "Track-Only",
"errorWithHttpStatusCode": "Foutmelding {}",
"versionCorrectionDisabled": "Versiecorrectie uitgeschakeld (de plug-in lijkt niet te werken)",
"unknown": "Onbekend",
"none": "Geen",
"never": "Nooit",
"latestVersionX": "Laatste versie: {}",
"installedVersionX": "Geïnstalleerde versie: {}",
"lastUpdateCheckX": "Laatste updatecontrole: {}",
"remove": "Verwijderen",
"yesMarkUpdated": "Ja, markeer als bijgewerkt",
"fdroid": "F-Droid Official",
"appIdOrName": "App ID of naam",
"appId": "App ID",
"appWithIdOrNameNotFound": "Er werd geen app gevonden met dat ID of die naam",
"reposHaveMultipleApps": "Repositories kunnen meerdere apps bevatten",
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
"steam": "Steam",
"steamMobile": "Steam Mobile",
"steamChat": "Steam Chat",
"install": "Installeren",
"markInstalled": "Als geïnstalleerd markere",
"update": "Update",
"markUpdated": "Markeren als bijgewerkt",
"additionalOptions": "Aanvullende opties",
"disableVersionDetection": "Versieherkenning uitschakelen",
"noVersionDetectionExplanation": "Deze optie moet alleen worden gebruikt voor apps waar versieherkenning niet correct werkt.",
"downloadingX": "Downloaden {}",
"downloadNotifDescription": "Stelt de gebruiker op de hoogte van de voortgang bij het downloaden van een app",
"noAPKFound": "Geen APK gevonden",
"noVersionDetection": "Geen versieherkenning",
"categorize": "Categoriseren",
"categories": "Categorieën",
"category": "Categorie",
"noCategory": "Geen categorie",
"noCategories": "Geen categorieën",
"deleteCategoriesQuestion": "Categorieën verwijderen?",
"categoryDeleteWarning": "Alle apps in verwijderde categorieën worden teruggezet naar 'ongecategoriseerd'.",
"addCategory": "Categorie toevoegen",
"label": "Label",
"language": "Taal",
"copiedToClipboard": "Gekopieerd naar klembord",
"storagePermissionDenied": "Toegang tot opslag geweigerd",
"selectedCategorizeWarning": "Dit zal eventuele bestaande categorie-instellingen voor de geselecteerde apps vervangen.",
"filterAPKsByRegEx": "Filter APK's op reguliere expressie",
"removeFromObtainium": "Verwijder van Obtainium",
"uninstallFromDevice": "Verwijder van apparaat",
"onlyWorksWithNonVersionDetectApps": "Werkt alleen voor apps waarbij versieherkenning is uitgeschakeld.",
"releaseDateAsVersion": "Gebruik de releasedatum als versie",
"releaseDateAsVersionExplanation": "Deze optie moet alleen worden gebruikt voor apps waar versieherkenning niet correct werkt, maar waar wel een releasedatum beschikbaar is.",
"changes": "Veranderingen",
"releaseDate": "Releasedatum",
"importFromURLsInFile": "Importeren vanaf URL's in een bestand (zoals OPML)",
"versionDetection": "Versieherkenning",
"standardVersionDetection": "Standaard versieherkenning",
"groupByCategory": "Groepeer op categorie",
"autoApkFilterByArch": "Poging om APK's te filteren op CPU-architectuur indien mogelijk",
"overrideSource": "Bron overschrijven",
"dontShowAgain": "Don't show this again",
"dontShowTrackOnlyWarnings": "Geen waarschuwingen voor 'Track-Only' weergeven",
"dontShowAPKOriginWarnings": "APK-herkomstwaarschuwingen niet weergeven",
"moveNonInstalledAppsToBottom": "Verplaats niet-geïnstalleerde apps naar de onderkant van de apps-weergave",
"gitlabPATLabel": "GitLab Personal Access Token\n(Maakt het mogelijk beter te zoeken naar APK's)",
"about": "Over",
"requiresCredentialsInSettings": "Dit vereist aanvullende referenties (in Instellingen)",
"checkOnStart": "Controleren op updates bij opstarten",
"tryInferAppIdFromCode": "Probeer de app-ID af te leiden uit de broncode",
"removeOnExternalUninstall": "Automatisch extern verwijderde apps verwijderen",
"pickHighestVersionCode": "Automatisch de APK met de hoogste versiecode selecteren",
"checkUpdateOnDetailPage": "Controleren op updates bij het openen van een app-detailpagina",
"disablePageTransitions": "Schakel overgangsanimaties tussen pagina's uit",
"reversePageTransitions": "Omgekeerde overgangsanimaties tussen pagina's",
"minStarCount": "Minimale Github Stars",
"addInfoBelow": "Voeg deze informatie hieronder toe.",
"addInfoInSettings": "Voeg deze informatie toe in de instellingen.",
"githubSourceNote": "Beperkingen van GitHub kunnen worden vermeden door het gebruik van een API-sleutel.",
"gitlabSourceNote": "GitLab APK-extractie werkt mogelijk niet zonder een API-sleutel.",
"sortByFileNamesNotLinks": "Sorteren op bestandsnamen in plaats van volledige links.",
"filterReleaseNotesByRegEx": "Filter release-opmerkingen met een reguliere expressie.",
"customLinkFilterRegex": "Aangepaste APK-linkfilter met een reguliere expressie (Standaard '.apk$').",
"appsPossiblyUpdated": "Poging tot app-updates",
"appsPossiblyUpdatedNotifDescription": "Stelt de gebruiker op de hoogte dat updates voor één of meer apps mogelijk in de achtergrond zijn toegepast",
"xWasPossiblyUpdatedToY": "{} mogelijk bijgewerkt naar {}.",
"enableBackgroundUpdates": "Achtergrondupdates inschakelen",
"backgroundUpdateReqsExplanation": "Achtergrondupdates zijn mogelijk niet voor alle apps mogelijk.",
"backgroundUpdateLimitsExplanation": "THet succes van een installatie in de achtergrond kan alleen worden bepaald wanneer Obtainium is geopend.",
"verifyLatestTag": "Verifieer de 'Laatste'-tag",
"intermediateLinkRegex": "Filter voor een 'tussenliggende' link om eerst te bezoeken",
"intermediateLinkNotFound": "Tussenliggende link niet gevonden",
"exemptFromBackgroundUpdates": "Vrijgesteld van achtergrondupdates (indien ingeschakeld)",
"bgUpdatesOnWiFiOnly": "Achtergrondupdates uitschakelen wanneer niet verbonden met WiFi",
"autoSelectHighestVersionCode": "Automatisch de APK met de hoogste versiecode selecteren",
"versionExtractionRegEx": "Reguliere expressie voor versie-extractie",
"matchGroupToUse": "Overeenkomende groep om te gebruiken voor de reguliere expressie voor versie-extractie",
"highlightTouchTargets": "Markeer minder voor de hand liggende aanraakdoelen.",
"pickExportDir": "Kies de exportmap",
"autoExportOnChanges": "Automatisch exporteren bij wijzigingen",
"filterVersionsByRegEx": "Filter versies met een reguliere expressie",
"trySelectingSuggestedVersionCode": "Probeer de voorgestelde versiecode APK te selecteren",
"dontSortReleasesList": "Volgorde van releases behouden vanuit de API",
"reverseSort": "Sortering omkeren",
"debugMenu": "Debug menu",
"bgTaskStarted": "Achtergrondtaak gestart - controleer de logs.",
"runBgCheckNow": "Voer nu een achtergrondupdatecontrole uit",
"versionExtractWholePage": "De reguliere expressie voor versie-extractie toepassen op de hele pagina",
"installing": "Installeren",
"skipUpdateNotifications": "Updatemeldingen overslaan",
"updatesAvailableNotifChannel": "Updates beschikbaar",
"appsUpdatedNotifChannel": "Apps bijgewerkt",
"appsPossiblyUpdatedNotifChannel": "Poging tot app-updates",
"errorCheckingUpdatesNotifChannel": "Foutcontrole bij het zoeken naar updates",
"appsRemovedNotifChannel": "Apps verwijderd",
"downloadingXNotifChannel": "{} downloaden",
"completeAppInstallationNotifChannel": "Voltooien van de app-installatie",
"checkingForUpdatesNotifChannel": "Controleren op updates",
"onlyCheckInstalledOrTrackOnlyApps": "Alleen geïnstalleerde en Track-Only apps controleren op updates",
"removeAppQuestion": {
"one": "App verwijderen?",
"other": "Apps verwijderen?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "Te veel verzoeken (aantal beperkt) - probeer het opnieuw in {} minuut",
"other": "Te veel verzoeken (aantal beperkt) - probeer het opnieuw in {} minuten"
},
"bgUpdateGotErrorRetryInMinutes": {
"one": "Achtergrondupdatecontrole heeft een {}, zal een hercontrole plannen over {} minuut",
"other": "Achtergrondupdatecontrole heeft een {}, zal een hercontrole plannen over {} minuten"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "Achtergrondupdatecontrole heeft {} update gevonden - zal de gebruiker op de hoogte stellen indien nodig",
"other": "Achtergrondupdatecontrole heeft {} updates gevonden - zal de gebruiker op de hoogte stellen indien nodig"
},
"apps": {
"one": "{} app",
"other": "{} apps"
},
"url": {
"one": "{} URL",
"other": "{} URLs"
},
"minute": {
"one": "{} minuut",
"other": "{} minuten"
},
"hour": {
"one": "{} uur",
"other": "{} uren"
},
"day": {
"one": "{} dag",
"other": "{} dagen"
},
"clearedNLogsBeforeXAfterY": {
"one": "{n} logboekitem gewist (voor = {before}, na = {after})",
"other": "{n} logboekitems gewist (voor = {before}, na = {after})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} en nog 1 app hebben updates.",
"other": "{} en {} meer apps hebben updates."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} en nog 1 app is bijgewerkt.",
"other": "{} en {} meer apps zijn bijgewerkt."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} en nog 1 app zijn mogelijk bijgewerkt.",
"other": "{} en {} meer apps zijn mogelijk bijgwerkt."
}
}

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 {}",
"noReleaseFound": "Nie można znaleźć odpowiedniego wydania",
"noVersionFound": "Nie można określić wersji wydania",
@ -22,12 +12,11 @@
"ok": "Okej",
"and": "i",
"githubPATLabel": "Osobisty token dostępu GitHub (zwiększa limit zapytań)",
"githubPATHint": "Wymagany format: użytkownik:token",
"githubPATFormat": "użytkownik:token",
"includePrereleases": "Uwzględnij wersje wstępne",
"fallbackToOlderReleases": "Powracaj do starszych wersji",
"filterReleaseTitlesByRegEx": "Filtruj tytuły wydań wg. wyrażeń regularnych",
"invalidRegEx": "Nieprawidłowe wyrażenie regularne",
"noDescription": "Brak opisu",
"cancel": "Anuluj",
"continue": "Kontynuuj",
"requiredInBrackets": "(Wymagane)",
@ -36,10 +25,10 @@
"githubStarredRepos": "Repozytoria GitHub oznaczone gwiazdką",
"uname": "Nazwa użytkownika",
"wrongArgNum": "Nieprawidłowa liczba podanych argumentów",
"xIsTrackOnly": "{} jest tylko obserwowana",
"xIsTrackOnly": "{} jest tylko obserwowane",
"source": "Źródło",
"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”.",
"trackOnlyAppDescription": "Aplikacja będzie obserwowana pod kątem aktualizacji, ale Obtainium nie będzie w stanie jej pobrać ani zainstalować.",
"cancelled": "Anulowano",
@ -52,9 +41,9 @@
"searchSomeSourcesLabel": "Szukaj (tylko niektóre źródła)",
"search": "Szukaj",
"additionalOptsFor": "Dodatkowe opcje dla {}",
"supportedSourcesBelow": "Obsługiwane źródła:",
"supportedSources": "Obsługiwane źródła",
"trackOnlyInBrackets": "(tylko obserwowane)",
"searchableInBrackets": "(Wyszukiwalne)",
"searchableInBrackets": "(wyszukiwalne)",
"appsString": "Aplikacje",
"noApps": "Brak aplikacji",
"noAppsForFilter": "Brak aplikacji dla filtra",
@ -72,11 +61,12 @@
"removeSelectedApps": "Usuń wybrane aplikacje",
"updateX": "Zaktualizuj {}",
"installX": "Zainstaluj {}",
"markXTrackOnlyAsUpdated": "Oznacz {}\n(Tylko obserwowana)\njako zaktualizowaną",
"markXTrackOnlyAsUpdated": "Oznacz {}\n(tylko obserwowana)\njako zaktualizowaną",
"changeX": "Zmień {}",
"installUpdateApps": "Instaluj/aktualizuj aplikacje",
"installUpdateSelectedApps": "Zainstaluj/zaktualizuj wybrane aplikacje",
"markXSelectedAppsAsUpdated": "Oznaczyć {} wybranych aplikacji jako zaktualizowane?",
"no": "Nie",
"yes": "Tak",
"markSelectedAppsUpdated": "Oznacz wybrane aplikacje jako zaktualizowane",
"pinToTop": "Przypnij",
@ -113,6 +103,7 @@
"importErrors": "Błędy importowania",
"importedXOfYApps": "Zaimportowano {} z {} aplikacji.",
"followingURLsHadErrors": "Następujące adresy URL zawierały błędy:",
"okay": "Okej",
"selectURL": "Wybierz adres URL",
"selectURLs": "Wybierz adresy URL",
"pick": "Wybierz",
@ -151,20 +142,20 @@
"warning": "Uwaga",
"sourceIsXButPackageFromYPrompt": "Źródłem aplikacji jest '{}', ale pakiet wydania pochodzi z '{}'. Kontynuować?",
"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.",
"xHasAnUpdate": "{} ma aktualizację.",
"appsUpdated": "Zaktualizowane aplikacje",
"appsUpdatedNotifDescription": "Powiadamia użytkownika, gdy jedna lub więcej aplikacji zostało zaktualizowanych w tle",
"appsUpdated": "Zaktualizowano aplikacje",
"appsUpdatedNotifDescription": "Informuje, gdy co najmniej jedna aplikacja została zaktualizowana w tle",
"xWasUpdatedToY": "{} zaktualizowano do {}.",
"errorCheckingUpdates": "Sprawdzanie błędów aktualizacji",
"errorCheckingUpdatesNotifDescription": "Powiadomienie wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się",
"errorCheckingUpdates": "Błąd sprawdzania aktualizacji",
"errorCheckingUpdatesNotifDescription": "Jest wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się",
"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: {}",
"completeAppInstallation": "Ukończenie instalacji aplikacji",
"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",
"checkingForUpdatesNotifDescription": "Tymczasowe powiadomienie pojawiające się podczas sprawdzania aktualizacji",
"pleaseAllowInstallPerm": "Pozwól Obtainium instalować aplikacje",
@ -181,6 +172,7 @@
"yesMarkUpdated": "Tak, oznacz jako zaktualizowane",
"fdroid": "Oficjalny F-Droid",
"appIdOrName": "ID aplikacji lub nazwa",
"appId": "ID aplikacji",
"appWithIdOrNameNotFound": "Nie znaleziono aplikacji o tym identyfikatorze lub nazwie",
"reposHaveMultipleApps": "Repozytoria mogą zawierać wiele aplikacji",
"fdroidThirdPartyRepo": "Zewnętrzne repo F-Droid",
@ -195,7 +187,7 @@
"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.",
"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",
"noVersionDetection": "Bez wykrywania wersji",
"categorize": "Kategoryzuj",
@ -247,14 +239,42 @@
"sortByFileNamesNotLinks": "Sortuj wg nazw plików zamiast pełnych linków",
"filterReleaseNotesByRegEx": "Filtruj informacje o wersji według wyrażenia regularnego",
"customLinkFilterRegex": "Filtruj linki APK według wyrażenia regularnego (domyślnie \".apk$\")",
"appsPossiblyUpdated": "Informuj o próbach aktualizacji",
"appsPossiblyUpdatedNotifDescription": "Powiadamiaj o potencjalnym zastosowaniu w tle aktualizacji jednej lub większej ilości aplikacji",
"appsPossiblyUpdated": "Aplikacje mogły zostać zaktualizowane",
"appsPossiblyUpdatedNotifDescription": "Powiadamia, gdy co najmniej jedna aktualizacja aplikacji została potencjalnie zastosowana w tle",
"xWasPossiblyUpdatedToY": "{} być może zaktualizowano do {}.",
"enableBackgroundUpdates": "Włącz aktualizacje w tle",
"backgroundUpdateReqsExplanation": "Aktualizacje w tle mogą nie być możliwe dla wszystkich aplikacji.",
"backgroundUpdateLimitsExplanation": "Powodzenie instalacji w tle można określić dopiero po otwarciu Obtainium.",
"verifyLatestTag": "Zweryfikuj najnowszy tag",
"intermediateLinkRegex": "Filtr linków \"pośrednich\" do odwiedzenia w pierwszej kolejności",
"intermediateLinkNotFound": "Nie znaleziono linku pośredniego",
"exemptFromBackgroundUpdates": "Wyklucz z uaktualnień w tle (jeśli są włączone)",
"bgUpdatesOnWiFiOnly": "Wyłącz aktualizacje w tle, gdy nie ma połączenia z Wi-Fi",
"autoSelectHighestVersionCode": "Automatycznie wybierz najwyższy kod wersji APK",
"versionExtractionRegEx": "Wyrażenie regularne wyodrębniające wersję",
"matchGroupToUse": "Dopasuj grupę do użycia dla wyrażenia regularnego wyodrębniania wersji",
"highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
"pickExportDir": "Wybierz katalog eksportu",
"autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
"filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego",
"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",
"onlyCheckInstalledOrTrackOnlyApps": "Sprawdzaj tylko zainstalowane i obserwowane aplikacje pod kątem aktualizacji",
"removeAppQuestion": {
"one": "Usunąć aplikację?",
"few": "Usunąć aplikacje?",
@ -267,6 +287,12 @@
"many": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} minut",
"other": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} minuty"
},
"bgUpdateGotErrorRetryInMinutes": {
"one": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minutę",
"few": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minuty",
"many": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minut",
"other": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minuty"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "W tle znaleziono {} aktualizację - w razie potrzeby użytkownik zostanie o tym powiadomiony",
"few": "W tle znaleziono {} aktualizacje - w razie potrzeby użytkownik zostanie o tym powiadomiony",
@ -326,8 +352,5 @@
"few": "{} i {} inne apki mogły zostać zaktualizowane.",
"many": "{} i {} innych apek mogło zostać zaktualizowanych.",
"other": "{} i {} inne apki mogły zostać zaktualizowane."
},
"enableBackgroundUpdates": "Włącz aktualizacje w tle",
"intermediateLinkRegex": "Filtr linków \"pośrednich\" do odwiedzenia w pierwszej kolejności",
"intermediateLinkNotFound": "Nie znaleziono linku pośredniego"
}
}
}

View File

@ -12,8 +12,6 @@
"ok": "Ok",
"and": "e",
"githubPATLabel": "Token de Acceso Pessoal do GitHub (Reduz tempos de espera)",
"githubPATHint": "O TAP deve estar nesse formato: usuario:token",
"githubPATFormat": "usuario:token",
"includePrereleases": "Incluir pré-lançamentos",
"fallbackToOlderReleases": "Retornar para versões anteriores",
"filterReleaseTitlesByRegEx": "Filtrar Titulos de Versões por Expressão Regular",
@ -43,7 +41,7 @@
"searchSomeSourcesLabel": "Procurar (Apenas Algumas Fontes)",
"search": "Procurar",
"additionalOptsFor": "Opções Adicionais para {}",
"supportedSourcesBelow": "Fontes Compatíveis:",
"supportedSources": "Fontes Compatíveis",
"trackOnlyInBrackets": "(Apenas Seguir)",
"searchableInBrackets": "(Pesquisável)",
"appsString": "Apps",
@ -130,7 +128,7 @@
"pinUpdates": "Fixar atualizações no topo da visão de Apps",
"updates": "Atualizações",
"sourceSpecific": "Específico a fonte",
"appSource": "Fonte de Apps",
"appSource": "Fonte do App",
"noLogs": "Sem Logs",
"appLogs": "Logs do App",
"close": "Fechar",
@ -252,6 +250,31 @@
"intermediateLinkNotFound": "Link intermediário não encontrado",
"exemptFromBackgroundUpdates": "Isento de atualizações em segundo plano (se ativadas)",
"bgUpdatesOnWiFiOnly": "Desative atualizações em segundo plano quando não estiver em WiFi",
"autoSelectHighestVersionCode": "Auto-selecionar o maior codigo de versão",
"versionExtractionRegEx": "RegEx para Extração de Versão",
"matchGroupToUse": "Grupo de Seleção para Usar",
"highlightTouchTargets": "Destaque areas de toque menos óbvias",
"pickExportDir": "Escolher Diretorio de Exportação",
"autoExportOnChanges": "Auto-exportar em mudanças",
"filterVersionsByRegEx": "Filtrar Versões por Expressão Regular",
"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": "Aplicar Regex de Extração de Versão à Página Inteira",
"installing": "Instalando",
"skipUpdateNotifications": "Pular notificações de update",
"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",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"removeAppQuestion": {
"one": "Remover App?",
"other": "Remover Apps?"
@ -304,4 +327,4 @@
"one": "{} e 1 outro app pode ter sido atualizado.",
"other": "{} e {} outros apps podem ter sido atualizados."
}
}
}

View File

@ -1,5 +1,5 @@
{
"invalidURLForSource": "Неверный URL-адрес {} приложения",
"invalidURLForSource": "Неверный URL-адрес приложения: {}",
"noReleaseFound": "Не удалось найти подходящий релиз",
"noVersionFound": "Не удалось определить версию релиза",
"urlMatchesNoSource": "URL-адрес не соответствует известному источнику",
@ -9,43 +9,41 @@
"placeholder": "Заполнитель",
"someErrors": "Возникли некоторые ошибки",
"unexpectedError": "Неожиданная ошибка",
"ok": "Окей",
"ok": "Ok",
"and": "и",
"githubPATLabel": "Персональный токен доступа GitHub (увеличивает лимит запросов)",
"githubPATHint": "Токен доступа должен быть в формате: имя_пользователя:токен",
"githubPATFormat": "имя_пользователя:токен",
"githubPATLabel": "Персональный токен доступа GitHub\n(увеличивает лимит запросов)",
"includePrereleases": "Включить предварительные релизы",
"fallbackToOlderReleases": "Откатиться к более старым версиям",
"filterReleaseTitlesByRegEx": "Фильтровать заголовки релизов\nс помощью регулярного выражения",
"fallbackToOlderReleases": "Откатываться к предыдущей версии",
"filterReleaseTitlesByRegEx": "Фильтровать заголовки релизов\n(регулярное выражение)",
"invalidRegEx": "Неверное регулярное выражение",
"noDescription": "Нет описания",
"cancel": "Отмена",
"continue": "Продолжить",
"requiredInBrackets": "(Обязательно)",
"dropdownNoOptsError": "Ошибка: Выпадающий список должен содержать хотя бы одну опцию",
"requiredInBrackets": "(обязательно)",
"dropdownNoOptsError": "Ошибка: в выпадающем списке должна быть выбрана хотя бы одна настройка",
"colour": "Цвет",
"githubStarredRepos": "Помеченные звездочкой репозитории на GitHub",
"githubStarredRepos": "Избранные репозитории GitHub",
"uname": "Имя пользователя",
"wrongArgNum": "Неправильное количество предоставленных аргументов",
"xIsTrackOnly": "{} только для отслеживания",
"source": "Источник",
"app": "Приложение",
"appsFromSourceAreTrackOnly": "Приложения из этого источника являются 'только для отслеживания'.",
"youPickedTrackOnly": "Вы выбрали опцию 'Только для отслеживания'.",
"trackOnlyAppDescription": "Приложение будет отслеживаться на предмет обновлений, но Obtainium не сможет загрузить или установить его.",
"appsFromSourceAreTrackOnly": "Приложения из этого источника настроены только для отслеживания",
"youPickedTrackOnly": "Вы выбрали опцию 'Только для отслеживания'",
"trackOnlyAppDescription": "Приложение будет отслеживаться на предмет обновлений, но Obtainium не сможет загрузить или установить его",
"cancelled": "Отменено",
"appAlreadyAdded": "Приложение уже добавлено",
"alreadyUpToDateQuestion": "Приложение уже обновлено?",
"addApp": "Добавить приложение",
"addApp": "Добавить",
"appSourceURL": "URL-источник приложения",
"error": "Ошибка",
"add": "Добавить",
"searchSomeSourcesLabel": "Поиск (только в некоторых источниках)",
"searchSomeSourcesLabel": "Поиск (в некоторых источниках)",
"search": "Поиск",
"additionalOptsFor": "Дополнительные опции для {}",
"supportedSourcesBelow": "Поддерживаемые источники:",
"trackOnlyInBrackets": "(Только для отслеживания)",
"searchableInBrackets": "(Поиск)",
"additionalOptsFor": "Дополнительные настройки для {}",
"supportedSources": "Поддерживаемые источники",
"trackOnlyInBrackets": "(только отслеживание)",
"searchableInBrackets": "(поиск)",
"appsString": "Приложения",
"noApps": "Нет приложений",
"noAppsForFilter": "Нет приложений для фильтра",
@ -56,9 +54,9 @@
"estimateInBracketsShort": "(Оценка)",
"notInstalled": "Не установлено",
"estimateInBrackets": "(Оценка)",
"selectAll": "Выбрать все",
"selectAll": "Выбрать всё",
"deselectN": "Отменить выбор {}",
"xWillBeRemovedButRemainInstalled": "{} будет удалено из Obtainium, но останется установленным на устройстве.",
"xWillBeRemovedButRemainInstalled": "{} будет удалено из Obtainium, но останется на устройстве",
"removeSelectedAppsQuestion": "Удалить выбранные приложения?",
"removeSelectedApps": "Удалить выбранные приложения",
"updateX": "Обновить {}",
@ -67,17 +65,17 @@
"changeX": "Изменить {}",
"installUpdateApps": "Установить/Обновить приложения",
"installUpdateSelectedApps": "Установить/Обновить выбранные приложения",
"markXSelectedAppsAsUpdated": "Отметить {} выбранные приложения как обновленные?",
"markXSelectedAppsAsUpdated": "Выбрано приложений: {}. Отметить как обновлённые?",
"no": "Нет",
"yes": "Да",
"markSelectedAppsUpdated": "Отметить выбранные приложения как обновленные",
"markSelectedAppsUpdated": "Отметить выбранные приложения как обновлённые",
"pinToTop": "Закрепить сверху",
"unpinFromTop": "Открепить",
"resetInstallStatusForSelectedAppsQuestion": "Сбросить статус установки для выбранных приложений?",
"installStatusOfXWillBeResetExplanation": "Статус установки для выбранных приложений будет сброшен.\n\nЭто может помочь, если версия приложения, отображаемая в Obtainium, неправильная из-за неудачных обновлений или других проблем.",
"installStatusOfXWillBeResetExplanation": "Статус установки для выбранных приложений будет сброшен.\n\nЭто может помочь, если версия приложения, отображаемая в Obtainium, некорректная из-за неудачных обновлений или других проблем",
"shareSelectedAppURLs": "Поделиться выбранными URL-адресами приложений",
"resetInstallStatus": "Сбросить статус установки",
"more": "Еще",
"more": "Ещё",
"removeOutdatedFilter": "Удалить фильтр для устаревших приложений",
"showOutdatedOnly": "Показывать только устаревшие приложения",
"filter": "Фильтр",
@ -87,7 +85,7 @@
"author": "Автор",
"upToDateApps": "Приложения со свежими обновлениями",
"nonInstalledApps": "Неустановленные приложения",
"importExport": "Импорт/экспорт",
"importExport": "Данные",
"settings": "Настройки",
"exportedTo": "Экспортировано в {}",
"obtainiumExport": "Экспорт из Obtainium",
@ -101,63 +99,63 @@
"searchX": "Поиск {}",
"noResults": "Результатов не найдено",
"importX": "Импорт {}",
"importedAppsIdDisclaimer": "Импортированные приложения могут неверно отображаться как 'Не установлены'.\nДля исправления этой проблемы повторно установите их через Obtainium.\nЭто не должно повлиять на данные приложения.\n\nПроблемы возникают только при импорте из URL-адреса и сторонних источников.",
"importedAppsIdDisclaimer": "Импортированные приложения могут неверно отображаться как неустановленные.\nДля исправления этой проблемы повторно установите их через Obtainium.\nЭто не должно повлиять на данные приложения.\n\nПроблемы возникают только при импорте из URL-адреса и сторонних источников",
"importErrors": "Ошибка импорта",
"importedXOfYApps": "Импортировано {} из {} приложений.",
"importedXOfYApps": "Импортировано приложений: {} из {}",
"followingURLsHadErrors": "При импорте следующие URL-адреса содержали ошибки:",
"okay": "Окей",
"okay": "Ok",
"selectURL": "Выбрать URL-адрес",
"selectURLs": "Выбрать URL-адреса",
"pick": "Выбрать",
"theme": "Тема",
"dark": "Темная",
"dark": "Тёмная",
"light": "Светлая",
"followSystem": "Как в системе",
"followSystem": "Системная",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "Использовать чёрную тему",
"appSortBy": "Сортировка приложений по",
"appSortBy": "Сортировка приложений",
"authorName": "Автор/Название",
"nameAuthor": "Название/Автор",
"asAdded": "В порядке добавления",
"appSortOrder": "Порядок сортировки приложений",
"appSortOrder": "Порядок",
"ascending": "По возрастанию",
"descending": "По убыванию",
"bgUpdateCheckInterval": "Интервал проверки обновлений в фоновом режиме",
"neverManualOnly": "Никогда - Только вручную",
"neverManualOnly": "Никогда — только вручную",
"appearance": "Внешний вид",
"showWebInAppView": "Показывать исходную веб-страницу в представлении приложения",
"pinUpdates": "Закрепить обновления сверху списка приложений",
"showWebInAppView": "Показывать исходную веб-страницу на странице приложения",
"pinUpdates": "Отображать обновления приложений сверху списка",
"updates": "Обновления",
"sourceSpecific": "Специфика источника",
"appSource": "Источник приложения",
"sourceSpecific": "Настройки источников",
"appSource": "Исходный код",
"noLogs": "Нет журналов",
"appLogs": "Журналы приложений",
"appLogs": "Логи",
"close": "Закрыть",
"share": "Поделиться",
"appNotFound": "Приложение не найдено",
"obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "Выберите APK-файл",
"appHasMoreThanOnePackage": "{} имеет более одного пакета:",
"deviceSupportsXArch": "Ваше устройство поддерживает архитектуру процессора {}.",
"deviceSupportsXArch": "Ваше устройство поддерживает архитектуру процессора {}",
"deviceSupportsFollowingArchs": "Ваше устройство поддерживает следующие архитектуры процессора:",
"warning": "Предупреждение",
"sourceIsXButPackageFromYPrompt": "Источник приложения - '{}', но пакет для установки получен из '{}'. Продолжить?",
"sourceIsXButPackageFromYPrompt": "Источник приложения '{}', но пакет для установки получен из '{}'. Продолжить?",
"updatesAvailable": "Доступны обновления",
"updatesAvailableNotifDescription": "Уведомляет пользователя о наличии обновлений для одного или нескольких приложений, отслеживаемых Obtainium",
"noNewUpdates": "Нет новых обновлений.",
"xHasAnUpdate": "{} есть обновление.",
"updatesAvailableNotifDescription": "Уведомляет о наличии обновлений для одного или нескольких приложений в Obtainium",
"noNewUpdates": "Нет новых обновлений",
"xHasAnUpdate": "{} есть обновление",
"appsUpdated": "Приложения обновлены",
"appsUpdatedNotifDescription": "Уведомляет пользователя о том, что обновления для одного или нескольких приложений были применены в фоновом режиме",
"xWasUpdatedToY": "{} была обновлена до версии {}.",
"appsUpdatedNotifDescription": "Уведомляет об обновлении одного или нескольких приложений в фоновом режиме",
"xWasUpdatedToY": "{} была обновлена до версии {}",
"errorCheckingUpdates": "Ошибка при проверке обновлений",
"errorCheckingUpdatesNotifDescription": "Уведомление, которое появляется, когда проверка обновлений в фоновом режиме завершилась с ошибкой",
"errorCheckingUpdatesNotifDescription": "Уведомление о завершении проверки обновлений в фоновом режиме с ошибкой",
"appsRemoved": "Приложение удалено",
"appsRemovedNotifDescription": "Уведомляет пользователя о том, что одно или несколько приложений было удалено из-за ошибок при их загрузке",
"appsRemovedNotifDescription": "Уведомление об удалении одного или несколько приложений из-за ошибок при их загрузке",
"xWasRemovedDueToErrorY": "{} был удален из-за ошибки: {}",
"completeAppInstallation": "Завершение установки приложения",
"obtainiumMustBeOpenToInstallApps": "Для установки приложений Obtainium должен быть открыт",
"completeAppInstallationNotifDescription": "Просит пользователя вернуться в Obtainium, чтобы завершить установку приложения",
"obtainiumMustBeOpenToInstallApps": "Obtainium должен быть открыт для установки приложений",
"completeAppInstallationNotifDescription": "Уведомление о необходимости открыть Obtainium для завершения установки приложения",
"checkingForUpdates": "Проверка обновлений",
"checkingForUpdatesNotifDescription": "Временное уведомление, которое появляется при проверке обновлений",
"pleaseAllowInstallPerm": "Пожалуйста, разрешите Obtainium устанавливать приложения",
@ -169,14 +167,14 @@
"never": "Никогда",
"latestVersionX": "Последняя версия: {}",
"installedVersionX": "Установленная версия: {}",
"lastUpdateCheckX": "Последняя проверка обновлений: {}",
"lastUpdateCheckX": "Последняя проверка: {}",
"remove": "Удалить",
"yesMarkUpdated": "Да, отметить как обновленное",
"fdroid": "Официальный F-Droid",
"fdroid": "Официальные репозитории F-Droid",
"appIdOrName": "ID или название приложения",
"appId": "ID приложения",
"appWithIdOrNameNotFound": "Приложение с таким ID или названием не было найдено",
"reposHaveMultipleApps": "В хранилище может быть несколько приложений",
"reposHaveMultipleApps": "В хранилище несколько приложений",
"fdroidThirdPartyRepo": "Сторонние репозитории F-Droid",
"steam": "Steam",
"steamMobile": "Steam Mobile",
@ -184,121 +182,149 @@
"install": "Установить",
"markInstalled": "Пометить как установленное",
"update": "Обновить",
"markUpdated": "Отметить обновленным",
"additionalOptions": "Дополнительные опции",
"markUpdated": "Отметить обновлённым",
"additionalOptions": "Дополнительные настройки",
"disableVersionDetection": "Отключить обнаружение версии",
"noVersionDetectionExplanation": "Эта опция должна использоваться только для приложений, где обнаружение версии не работает корректно.",
"noVersionDetectionExplanation": "Эта настройка должна использоваться только для приложений, где обнаружение версии не работает корректно",
"downloadingX": "Загрузка {}",
"downloadNotifDescription": "Уведомляет пользователя о прогрессе загрузки приложения",
"noAPKFound": "APK не найден",
"noVersionDetection": "Версий не обнаружено",
"noVersionDetection": "Обнаружение версий отключено",
"categorize": "Категоризировать",
"categories": "Категории",
"category": "Категория",
"noCategory": "Без категории",
"noCategories": "Без категорий",
"deleteCategoriesQuestion": "Удалить категории?",
"categoryDeleteWarning": "Все приложения в удаленных категориях будут помечены как без категории.",
"categoryDeleteWarning": "Все приложения в удаленных категориях будут помечены как без категории",
"addCategory": "Добавить категорию",
"label": "Метка",
"language": "Язык",
"copiedToClipboard": "Скопировано в буфер обмена",
"storagePermissionDenied": "Отказано в доступе к хранилищу",
"selectedCategorizeWarning": "Это заменит все текущие настройки категорий для выбранных приложений.",
"filterAPKsByRegEx": "Фильтровать APK-файлы с помощью\nрегулярного выражения",
"selectedCategorizeWarning": "Это заменит все текущие настройки категорий для выбранных приложений",
"filterAPKsByRegEx": "Отфильтровать APK-файлы\n(регулярное выражение)",
"removeFromObtainium": "Удалить из Obtainium",
"uninstallFromDevice": "Удалить с устройства",
"onlyWorksWithNonVersionDetectApps": "Работает только для приложений с отключенным определением версии.",
"releaseDateAsVersion": "Использовать дату выпуска в качестве версии",
"releaseDateAsVersionExplanation": "Этот параметр следует использовать только для приложений, в которых определение версии не работает правильно, но имеется дата выпуска.",
"onlyWorksWithNonVersionDetectApps": "Работает только для приложений с отключенным определением версии",
"releaseDateAsVersion": "Дата выпуска вместо версии",
"releaseDateAsVersionExplanation": "Этот параметр следует использовать только для приложений, в которых определение версии не работает правильно, но имеется дата выпуска",
"changes": "Изменения",
"releaseDate": "Дата выпуска",
"importFromURLsInFile": "Импорт URL-адресов из файла (например, OPML)",
"importFromURLsInFile": "Импорт из файла URL-адресов (например: OPML)",
"versionDetection": "Определение версии",
"standardVersionDetection": "Стандартное определение версии",
"standardVersionDetection": "Стандартное",
"groupByCategory": "Группировать по категориям",
"autoApkFilterByArch": "Попытка фильтрации APK-файлов по архитектуре процессора, если это возможно",
"autoApkFilterByArch": "Попытаться отфильтровать APK-файлы по архитектуре процессора",
"overrideSource": "Переопределить источник",
"dontShowAgain": "Не показывать снова",
"dontShowTrackOnlyWarnings": "Не показывать предупреждения о только отслеживаемых приложениях",
"dontShowAPKOriginWarnings": "Не показывать предупреждения об источнике APK-файлов",
"moveNonInstalledAppsToBottom": "Переместить неустановленные приложения вниз списка",
"gitlabPATLabel": "Персональный токен доступа GitLab\n(Включает поиск и улучшает обнаружение APK)",
"about": "О приложении",
"dontShowAPKOriginWarnings": "Не показывать предупреждения об отличающемся источнике APK-файлов",
"moveNonInstalledAppsToBottom": "Отображать неустановленные приложения внизу списка",
"gitlabPATLabel": "Персональный токен доступа GitLab\n(включает поиск и улучшает обнаружение APK)",
"about": "Описание",
"requiresCredentialsInSettings": "Для этого требуются дополнительные учетные данные (в настройках)",
"checkOnStart": "Проверять наличие обновлений при запуске",
"tryInferAppIdFromCode": "Попытаться определить ID приложения из исходного кода",
"removeOnExternalUninstall": "Автоматически убирать из списка удаленные извне приложения",
"pickHighestVersionCode": "Автовыбор кода наивысшей версии APK",
"checkUpdateOnDetailPage": "Проверять наличие обновлений при открытии страницы представления приложения",
"pickHighestVersionCode": "Автовыбор актуальной версии кода APK",
"checkUpdateOnDetailPage": "Проверять наличие обновлений при открытии страницы приложения",
"disablePageTransitions": "Отключить анимацию перехода между страницами",
"reversePageTransitions": "Реверс анимации перехода между страницами",
"minStarCount": "Минимальное количество звёзд",
"addInfoBelow": "Добавьте эту информацию ниже.",
"addInfoInSettings": "Добавьте эту информацию в Настройки.",
"githubSourceNote": "Лимит запросов GitHub можно обойти, используя ключ API.",
"gitlabSourceNote": "Извлечение APK из GitLab может не работать без ключа API.",
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
"appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verify the 'latest' tag",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"addInfoBelow": "Добавьте эту информацию ниже",
"addInfoInSettings": "Добавьте эту информацию в Настройки",
"githubSourceNote": "Используя ключ API можно обойти лимит запросов GitHub",
"gitlabSourceNote": "Без ключа API может не работать извлечение APK с GitLab",
"sortByFileNamesNotLinks": "Сортировать по именам файлов, а не ссылкам целиком",
"filterReleaseNotesByRegEx": "Фильтровать примечания к выпуску\n(регулярное выражение)",
"customLinkFilterRegex": "Пользовательский фильтр ссылок APK\n(регулярное выражение, по умолчанию: '.apk$')",
"appsPossiblyUpdated": "Попытки обновления приложений",
"appsPossiblyUpdatedNotifDescription": "Уведомление о возможных обновлениях одного или нескольких приложений в фоновом режиме",
"xWasPossiblyUpdatedToY": "{} возможно был обновлен до {}",
"enableBackgroundUpdates": "Включить обновления в фоне",
"backgroundUpdateReqsExplanation": "Фоновые обновления могут быть возможны не для всех приложений",
"backgroundUpdateLimitsExplanation": "Успешность фоновой установки можно определить только после открытия Obtainium",
"verifyLatestTag": "Проверять тег 'latest'",
"intermediateLinkRegex": "Фильтр промежуточных ссылок для первоочередного посещения\n(регулярное выражение)",
"intermediateLinkNotFound": "Промежуточная ссылка не найдена",
"exemptFromBackgroundUpdates": "Исключить из фоновых обновлений (если включено)",
"bgUpdatesOnWiFiOnly": "Отключить фоновые обновления, если нет соединения с Wi-Fi",
"autoSelectHighestVersionCode": "Автоматически выбирать APK с актуальной версией кода",
"versionExtractionRegEx": "Регулярное выражение для извлечения версии",
"matchGroupToUse": "Выберите группу для использования",
"highlightTouchTargets": "Выделить менее очевидные элементы управления касанием",
"pickExportDir": "Выбрать каталог для экспорта",
"autoExportOnChanges": "Автоэкспорт при изменениях",
"filterVersionsByRegEx": "Фильтровать версии по регулярному выражению",
"trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
"dontSortReleasesList": "Сохранить порядок релизов от API",
"reverseSort": "Обратная сортировка",
"debugMenu": "Меню отладки",
"bgTaskStarted": "Фоновая задача начата — проверьте журналы",
"runBgCheckNow": "Запустить проверку фонового обновления сейчас",
"versionExtractWholePage": "Применить регулярное выражение версии ко всей странице",
"installing": "Устанавливается",
"skipUpdateNotifications": "Не оповещать об обновлениях",
"updatesAvailableNotifChannel": "Доступны обновления",
"appsUpdatedNotifChannel": "Приложения обновлены",
"appsPossiblyUpdatedNotifChannel": "Попытки обновления приложений",
"errorCheckingUpdatesNotifChannel": "Ошибка при проверке обновлений",
"appsRemovedNotifChannel": "Приложение удалено",
"downloadingXNotifChannel": "Загрузка {}",
"completeAppInstallationNotifChannel": "Завершение установки приложения",
"checkingForUpdatesNotifChannel": "Проверка обновлений",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"removeAppQuestion": {
"one": "Удалить приложение?",
"other": "Удалить приложения?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "Слишком много запросов (ограничение скорости) - попробуйте снова через {} минуту",
"other": "Слишком много запросов (ограничение скорости) - попробуйте снова через {} минуты"
"one": "Слишком много запросов (ограничение скорости) попробуйте снова через {} минуту",
"other": "Слишком много запросов (ограничение скорости) попробуйте снова через {} минуты"
},
"bgUpdateGotErrorRetryInMinutes": {
"one": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет запланирована через {} минуту",
"other": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет запланирована через {} минуты"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "В ходе проверки обновления в фоновом режиме было обнаружено {} обновление - Пользователю будет отправлено уведомление, если это необходимо",
"other": "В ходе проверки обновления в фоновом режиме было обнаружено {} обновлений - Пользователю будет отправлено уведомление, если это необходимо"
"one": "В ходе проверки обновления в фоновом режиме было обнаружено {} обновление Пользователю будет отправлено уведомление, если это необходимо",
"other": "В ходе проверки обновления в фоновом режиме было обнаружено {} обновлений Пользователю будет отправлено уведомление, если это необходимо"
},
"apps": {
"one": "{} Приложение",
"other": "{} Приложений"
"one": "{} приложение",
"other": "{} приложений"
},
"url": {
"one": "{} URL-адрес",
"other": "{} URL-адреса"
},
"minute": {
"one": "{} Минута",
"other": "{} Минуты"
"one": "{} минута",
"other": "{} минуты"
},
"hour": {
"one": "{} Час",
"other": "{} Часов"
"one": "{} час",
"other": "{} часов"
},
"day": {
"one": "{} День",
"other": "{} Дней"
"one": "{} день",
"other": "{} дней"
},
"clearedNLogsBeforeXAfterY": {
"one": "Очищен {n} журнал (до = {before}, после = {after})",
"other": "Очищено {n} журналов (до = {before}, после = {after})"
},
"xAndNMoreUpdatesAvailable": {
"one": "У {} и еще 1 приложения есть обновление.",
"other": "У {} и ещё {} приложений есть обновления."
"one": "У {} и еще 1 приложения есть обновление",
"other": "У {} и ещё {} приложений есть обновления"
},
"xAndNMoreUpdatesInstalled": {
"one": "{} и еще 1 приложение были обновлены.",
"other": "{} и еще {} приложений были обновлены."
"one": "{} и ещё 1 приложение были обновлены",
"other": "{} и ещё {} приложений были обновлены"
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated."
"one": "{} и ещё 1 приложение могли быть обновлены",
"other": "{} и ещё {} приложений могли быть обновлены"
}
}

318
assets/translations/sv.json Normal file
View File

@ -0,0 +1,318 @@
{
"invalidURLForSource": "Inte giltig {} App-URL",
"noReleaseFound": "Kunde inte hitta en lämplig releaseversion",
"noVersionFound": "Kunde inte bestämma releaseversion",
"urlMatchesNoSource": "URL matchar inte känd källa",
"cantInstallOlderVersion": "Kan inte installera en äldre version av en app",
"appIdMismatch": "Nerladdat paket-ID matchar inte nuvarande App-ID",
"functionNotImplemented": "This class has not implemented this function",
"placeholder": "Platshållare",
"someErrors": "Några fel uppstod",
"unexpectedError": "Oväntat fel",
"ok": "Okej",
"and": "och",
"githubPATLabel": "GitHub Personal Access Token (Increases Rate Limit)",
"includePrereleases": "Inkludera förreleaser",
"fallbackToOlderReleases": "Fall tillbaka till äldre releaser",
"filterReleaseTitlesByRegEx": "Filter Release Titles by Regular Expression",
"invalidRegEx": "Invalid regular expression",
"noDescription": "Ingen beskrivning",
"cancel": "Avbryt",
"continue": "Fortsätt",
"requiredInBrackets": "(Kräver)",
"dropdownNoOptsError": "ERROR: DROPDOWN MUST HAVE AT LEAST ONE OPT",
"colour": "Färg",
"githubStarredRepos": "GitHub Stjärnmärkta Förråd",
"uname": "Användarnamn",
"wrongArgNum": "Wrong number of arguments provided",
"xIsTrackOnly": "{} är 'Följ-Endast'",
"source": "Källa",
"app": "App",
"appsFromSourceAreTrackOnly": "Apparna från denna källa är 'Följ-Endast'.",
"youPickedTrackOnly": "Du har markerat 'Följ-Endast'-alternativet",
"trackOnlyAppDescription": "Appen kommer följas för uppdateringar men Obtainium kommer inte ladda ner eller installera den.",
"cancelled": "Avbruten",
"appAlreadyAdded": "App redan tillagd",
"alreadyUpToDateQuestion": "App redan uppdaterad?",
"addApp": "Lägg till App",
"appSourceURL": "URL till Appkälla",
"error": "Fel",
"add": "Lägg till",
"searchSomeSourcesLabel": "Sök (Bara några källor)",
"search": "Sök",
"additionalOptsFor": "Ytterligare Alternativ för {}",
"supportedSources": "Stödda Källor",
"trackOnlyInBrackets": "(Följ-Endast)",
"searchableInBrackets": "(Sökbar)",
"appsString": "Appar",
"noApps": "Inga Appar",
"noAppsForFilter": "Inga Appar för Filter",
"byX": "Av {}",
"percentProgress": "Progress: {}%",
"pleaseWait": "Vänta",
"updateAvailable": "Uppdatering Tillgänglig",
"estimateInBracketsShort": "(Est.)",
"notInstalled": "Inte Installerad",
"estimateInBrackets": "(Uppskattning)",
"selectAll": "Välj Alla",
"deselectN": "Avmarkera {}",
"xWillBeRemovedButRemainInstalled": "{} kommer tas bort från Obtainium men kommer vara fortsatt installerad på enheten.",
"removeSelectedAppsQuestion": "Ta bort markerade Appar?",
"removeSelectedApps": "Ta bort markerade Appar",
"updateX": "Uppdatera {}",
"installX": "Installera {}",
"markXTrackOnlyAsUpdated": "Märk {}\n(Följ-Endast)\nsom Uppdaterad",
"changeX": "Byt {}",
"installUpdateApps": "Installera/Uppdatera Appar",
"installUpdateSelectedApps": "Installera/Uppdatera Markerade Appar",
"markXSelectedAppsAsUpdated": "Märk {} markerade Appar som Uppdaterade?",
"no": "Nej",
"yes": "Ja",
"markSelectedAppsUpdated": "Märk Valda Appar som Uppdaterade",
"pinToTop": "Nåla fast högst upp",
"unpinFromTop": "Avnåla",
"resetInstallStatusForSelectedAppsQuestion": "Återställ Installationsstatus för valda Appar?",
"installStatusOfXWillBeResetExplanation": "Installationsstatusen för de markerade apparna kommer återställas.\n\n Detta kan hjälpa när appversionen visad i Obtanium är fel på grund av misslyckade uppdateringar eller andra orsaker.",
"shareSelectedAppURLs": "Dela Valda Appars URL:er",
"resetInstallStatus": "Återställ Installationstatus",
"more": "Mer",
"removeOutdatedFilter": "Ta bort Utgånga App-filtret",
"showOutdatedOnly": "Visa Endast Utgånga Appar",
"filter": "Filter",
"filterActive": "Filter *",
"filterApps": "Filtrera Appar",
"appName": "Appnamn",
"author": "Utvecklare",
"upToDateApps": "Uppdaterade Appar",
"nonInstalledApps": "Icke-Installerade Appar",
"importExport": "Importera/Exportera",
"settings": "Inställningar",
"exportedTo": "Exporterad till {}",
"obtainiumExport": "Obtainiumexport",
"invalidInput": "Ogiltig inmatning",
"importedX": "Importerad {}",
"obtainiumImport": "Obtainium Import",
"importFromURLList": "Importera från URL-lista",
"searchQuery": "Sökförfrågan",
"appURLList": "App URL List",
"line": "Linje",
"searchX": "Sök {}",
"noResults": "Inga resultat",
"importX": "Importera {}",
"importedAppsIdDisclaimer": "Importerade Appar kan felaktigt visas som \"Inte Installerad\".\nFör att fixa detta återinstallera dem genom Obtainium.\nDetta skall inte påverka appdata.\n\n Påverkar endast URL:en och tredjepartsimportermetoder.",
"importErrors": "Importfel",
"importedXOfYApps": "{} av {} Appar importerade.",
"followingURLsHadErrors": "Följande URL:er hade fel:",
"okay": "Okej",
"selectURL": "Välj URL",
"selectURLs": "Välj URL:er",
"pick": "Välj",
"theme": "Tema",
"dark": "Mörkt",
"light": "Ljust",
"followSystem": "Följ System",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "Använd svart tema",
"appSortBy": "Sortera Appar via",
"authorName": "Utvecklare/Namn",
"nameAuthor": "Namn/Utvecklare",
"asAdded": "As Added",
"appSortOrder": "App Sort Order",
"ascending": "Stigande",
"descending": "Fallande",
"bgUpdateCheckInterval": "Bakgrundsuppdateringskollfrekvens",
"neverManualOnly": "Never - Manual Only",
"appearance": "Utseende",
"showWebInAppView": "Visa källans hemsida i appvyn",
"pinUpdates": "Fäst uppdateringar högst upp i appvyn",
"updates": "Uppdateringar",
"sourceSpecific": "Källspecifik",
"appSource": "Appkälla",
"noLogs": "Inga Loggar",
"appLogs": "Apploggar",
"close": "Stäng",
"share": "Dela",
"appNotFound": "App ej funnen",
"obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "Välj en APK",
"appHasMoreThanOnePackage": "{} har fler än ett paket:",
"deviceSupportsXArch": "Din enhet stödjer {} CPU-arkiktektur.",
"deviceSupportsFollowingArchs": "YDin enhet stödjer följande CPU-arkitekturer:",
"warning": "Varning",
"sourceIsXButPackageFromYPrompt": "Appens källa är '{}' men releasepaketet kommer från '{}'. Vill du fortsätta?",
"updatesAvailable": "Uppdateringar Tillgängliga",
"updatesAvailableNotifDescription": "Aviserar användaren att det finns uppdateringar tillgängaliga för en eller fler Appar som följs av Obtainium",
"noNewUpdates": "Inga nya uppdateringar.",
"xHasAnUpdate": "{} har en uppdatering.",
"appsUpdated": "Appar Uppdaterade",
"appsUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were applied in the background",
"xWasUpdatedToY": "{} uppdaterades till {}.",
"errorCheckingUpdates": "Fel vid uppdateringskoll",
"errorCheckingUpdatesNotifDescription": "En aviserings som visar när bakgrundsuppdateringarkollar misslyckas",
"appsRemoved": "Appar borttagna",
"appsRemovedNotifDescription": "Aviserar användaren när en eller fler Appar togs bort på grund av fel när de laddades",
"xWasRemovedDueToErrorY": "{} togs bort på grund av detta felet: {}",
"completeAppInstallation": "Gör klar appinstallation",
"obtainiumMustBeOpenToInstallApps": "Obtainium måste vara öppet för att installera Appar",
"completeAppInstallationNotifDescription": "Frågar användaren att återvända till Obtaiunium när appinstallation är klar",
"checkingForUpdates": "Kollar efter Uppdateringar",
"checkingForUpdatesNotifDescription": "Transient notification that appears when checking for updates",
"pleaseAllowInstallPerm": "Tillåt Obtanium att installera Appar",
"trackOnly": "Följ-Endast",
"errorWithHttpStatusCode": "Fel {}",
"versionCorrectionDisabled": "Versionskorrigering inaktiverat (plugin verkar inte fungera)",
"unknown": "Okänd",
"none": "None",
"never": "Aldrig",
"latestVersionX": "Senaste Version: {}",
"installedVersionX": "Installerad Version: {}",
"lastUpdateCheckX": "Senaste uppdateringskoll: {}",
"remove": "Ta bort",
"yesMarkUpdated": "Ja, Märk som Uppdaterad",
"fdroid": "F-Droid Officiell",
"appIdOrName": "App-ID eller Namn",
"appId": "App-ID",
"appWithIdOrNameNotFound": "Ingen App funnen med det namnet eller ID",
"reposHaveMultipleApps": "Förråd kan innehålla flera ApparR",
"fdroidThirdPartyRepo": "F-Droid Tredjeparts Förråd",
"steam": "Steam",
"steamMobile": "Steam Mobile",
"steamChat": "Steam Chat",
"install": "Installera",
"markInstalled": "Märk Installerad",
"update": "Uppdatera",
"markUpdated": "Märk Uppdaterad",
"additionalOptions": "Ytterligare Alternativ",
"disableVersionDetection": "Inaktivera versionsdetektering",
"noVersionDetectionExplanation": "This option should only be used for Apps where version detection does not work correctly.",
"downloadingX": "Laddar ner {}",
"downloadNotifDescription": "Notifies the user of the progress in downloading an App",
"noAPKFound": "Ingen APK funnen",
"noVersionDetection": "Ingen versiondetektering",
"categorize": "Kategorisera",
"categories": "Kategorier",
"category": "Kategori",
"noCategory": "Ingen Kategori",
"noCategories": "Inga Kategorier",
"deleteCategoriesQuestion": "Ta Bort Kategorier?",
"categoryDeleteWarning": "Alla Appar i de borttagna kategorierna kommer att märkas som okategoriserade.",
"addCategory": "Lägg till Kategori",
"label": "Label",
"language": "Språk",
"copiedToClipboard": "Kopierat till Urklipp",
"storagePermissionDenied": "Lagringsbehörighet nekad",
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
"removeFromObtainium": "Ta bort från Obtainium",
"uninstallFromDevice": "Avinstallera från Enheten",
"onlyWorksWithNonVersionDetectApps": "Fungerar bara för Appar med versionsdetektering inaktiverat..",
"releaseDateAsVersion": "Använd releasedatum som version",
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
"changes": "Ändringar",
"releaseDate": "Releasedatum",
"importFromURLsInFile": "Importera från URL:er i fil (som OPML)",
"versionDetection": "Versionsdetektering",
"standardVersionDetection": "Standardversionsdetektering",
"groupByCategory": "Gruppera via Kategori",
"autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible",
"overrideSource": "Överskrid Källa",
"dontShowAgain": "Visa inte detta igen",
"dontShowTrackOnlyWarnings": "Visa inte 'Följ-Endast' varningar",
"dontShowAPKOriginWarnings": "Visa inte APK-ursprung varningar",
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
"about": "Om",
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
"checkOnStart": "Kolla efter uppdateringar vid start",
"tryInferAppIdFromCode": "Try inferring App ID from source code",
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
"pickHighestVersionCode": "Auto-select highest version code APK",
"checkUpdateOnDetailPage": "Check for updates on opening an App detail page",
"disablePageTransitions": "Disable page transition animations",
"reversePageTransitions": "Reverse page transition animations",
"minStarCount": "Minsta antal stjärnmarkeringar",
"addInfoBelow": "Lägg till denna information nedanför.",
"addInfoInSettings": "Lägg till denna information i Inställningar.",
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
"appsPossiblyUpdated": "App Updates Attempted",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"enableBackgroundUpdates": "Aktivera Bakgrundsuppdateringar",
"backgroundUpdateReqsExplanation": "Bakgrundsuppdateringar är inte möjligt för alla appar.",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
"verifyLatestTag": "Verifiera 'senaste'-taggen",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
"intermediateLinkNotFound": "Intermediate link not found",
"exemptFromBackgroundUpdates": "Undta från bakgrundsuppdateringar (om aktiverad)",
"bgUpdatesOnWiFiOnly": "Inaktivera Bakgrundsuppdateringar utan WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"versionExtractionRegEx": "Version Extraction RegEx",
"matchGroupToUse": "Match Group to Use",
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Välj Exportsökväg",
"autoExportOnChanges": "Automatisk export vid ändringar",
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
"dontSortReleasesList": "Retain release order from API",
"reverseSort": "Omvänd sortering",
"debugMenu": "Felsökningsmeny",
"bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Kör Bakgrundsuppdateringskoll Nu",
"removeAppQuestion": {
"one": "Ta Bort App?",
"other": "Ta Bort Appar?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "Too many requests (rate limited) - try again in {} minute",
"other": "Too many requests (rate limited) - try again in {} minutes"
},
"bgUpdateGotErrorRetryInMinutes": {
"one": "BG update checking encountered a {}, will schedule a retry check in {} minute",
"other": "BG update checking encountered a {}, will schedule a retry check in {} minutes"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "BG update checking found {} update - will notify user if needed",
"other": "BG update checking found {} updates - will notify user if needed"
},
"apps": {
"one": "{} App",
"other": "{} Appar"
},
"url": {
"one": "{} URL",
"other": "{} URL:er"
},
"minute": {
"one": "{} Minut",
"other": "{} Minuter"
},
"hour": {
"one": "{} Timme",
"other": "{} Timmar"
},
"day": {
"one": "{} Dag",
"other": "{} Dagar"
},
"clearedNLogsBeforeXAfterY": {
"one": "Rensade {n} logg (före = {before}, efter = {after})",
"other": "Rensade {n} loggar (före = {before}, efter = {after})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} och 1 app till har tillgängliga uppdateringar.",
"other": "{} och {} appar till har tillgängliga uppdateringar."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} och 1 till app uppdaterades.",
"other": "{} och {} appar till uppdaterades."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} och 1 till app kan ha uppdaterats.",
"other": "{} och {} appar till kan ha uppdaterats."
}
}

View File

@ -12,8 +12,6 @@
"ok": "好的",
"and": "和",
"githubPATLabel": "GitHub 个人访问令牌(提升 API 请求限额)",
"githubPATHint": "个人访问令牌必须为“username:token”的格式",
"githubPATFormat": "username:token",
"includePrereleases": "包含预发行版",
"fallbackToOlderReleases": "将旧发行版作为备选",
"filterReleaseTitlesByRegEx": "使用正则表达式筛选发行标题",
@ -43,7 +41,7 @@
"searchSomeSourcesLabel": "搜索(仅支持部分来源)",
"search": "搜索",
"additionalOptsFor": "{} 的更多选项",
"supportedSourcesBelow": "支持的来源",
"supportedSources": "支持的来源",
"trackOnlyInBrackets": "(仅追踪)",
"searchableInBrackets": "(可搜索)",
"appsString": "应用列表",
@ -53,9 +51,9 @@
"percentProgress": "进度:{}%",
"pleaseWait": "请稍候",
"updateAvailable": "更新可用",
"estimateInBracketsShort": "(推测)",
"estimateInBracketsShort": "推测",
"notInstalled": "未安装",
"estimateInBrackets": "(推测)",
"estimateInBrackets": "推测",
"selectAll": "全选",
"deselectN": "取消选择 {}",
"xWillBeRemovedButRemainInstalled": "{} 将从 Obtainium 中删除,但仍安装在您的设备中。",
@ -240,16 +238,43 @@
"gitlabSourceNote": "未使用访问令牌时可能无法从 GitLab 获取 APK 文件。",
"sortByFileNamesNotLinks": "使用文件名代替链接进行排序",
"filterReleaseNotesByRegEx": "使用正则表达式筛选发行说明",
"customLinkFilterRegex": "使用正则表达式自定义链接筛选(默认模式为“.apk$”)",
"customLinkFilterRegex": "使用正则表达式筛选自定义来源 APK 文件链接\n未填写时默认匹配模式为“.apk$”)",
"appsPossiblyUpdated": "已尝试更新应用",
"appsPossiblyUpdatedNotifDescription": "当应用已尝试在后台更新时发送通知",
"xWasPossiblyUpdatedToY": "已尝试将 {} 更新至 {}。",
"xWasPossiblyUpdatedToY": "已尝试将{}更新至 {}。",
"enableBackgroundUpdates": "启用后台更新",
"backgroundUpdateReqsExplanation": "后台更新未必适用于所有的应用。",
"backgroundUpdateLimitsExplanation": "只有在启动 Obtainium 时才能确认安装是否成功。",
"verifyLatestTag": "验证“Latest”标签",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"intermediateLinkRegex": "筛选一个首先访问的“中转”链接(正则表达式)",
"intermediateLinkNotFound": "未找到“中转”链接",
"exemptFromBackgroundUpdates": "单独禁用后台更新(若已经全局启用)",
"bgUpdatesOnWiFiOnly": "未连接 Wi-Fi 时禁用后台更新",
"autoSelectHighestVersionCode": "自动选择版本号最高的 APK 文件",
"versionExtractionRegEx": "获取版本号的正则表达式",
"matchGroupToUse": "引用的捕获组",
"highlightTouchTargets": "突出展示不明显的触摸区域",
"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": "正在检查更新",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
"removeAppQuestion": {
"one": "是否删除应用?",
"other": "是否删除应用?"
@ -302,4 +327,4 @@
"one": "{} 和另外 1 个应用已尝试更新。",
"other": "{} 和另外 {} 个应用已尝试更新。"
}
}
}

View File

@ -4,7 +4,9 @@
CURR_DIR="$(pwd)"
trap "cd "$CURR_DIR"" EXIT
git fetch && git merge origin/main && git push # Typically run after a PR to main, so bring dev up to date
if [ -z "$1" ]; then
git fetch && git merge origin/main && git push # Typically run after a PR to main, so bring dev up to date
fi
rm ./build/app/outputs/flutter-apk/* 2>/dev/null # Get rid of older builds if any
flutter build apk && flutter build apk --split-per-abi # Build (both split and combined APKs)
for file in ./build/app/outputs/flutter-apk/*.sha1; do gpg --sign --detach-sig "$file"; done # Generate PGP signatures

View File

@ -25,12 +25,16 @@ class APKCombo extends AppSource {
}
@override
Map<String, String> get requestHeaders => {
"User-Agent": "curl/8.0.1",
"Accept": "*/*",
"Connection": "keep-alive",
"Host": "$host"
};
Future<Map<String, String>?> getRequestHeaders(
{Map<String, dynamic> additionalSettings = const <String, dynamic>{},
bool forAPKDownload = false}) async {
return {
"User-Agent": "curl/8.0.1",
"Accept": "*/*",
"Connection": "keep-alive",
"Host": "$host"
};
}
Future<List<MapEntry<String, String>>> getApkUrls(String standardUrl) async {
var res = await sourceRequest('$standardUrl/download/apk');

View File

@ -3,19 +3,38 @@ import 'package:html/parser.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
parseDateTimeMMMddCommayyyy(String? dateString) {
DateTime? releaseDate;
try {
releaseDate = dateString != null
? DateFormat('MMM dd, yyyy').parse(dateString)
: null;
releaseDate = dateString != null && releaseDate == null
? DateFormat('MMMM dd, yyyy').parse(dateString)
: releaseDate;
} catch (err) {
// ignore
}
return releaseDate;
}
class APKPure extends AppSource {
APKPure() {
host = 'apkpure.com';
allowSubDomains = true;
naiveStandardVersionDetection = true;
}
@override
String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegExB = RegExp('^https?://m.$host/+[^/]+/+[^/]+');
RegExp standardUrlRegExB =
RegExp('^https?://m.$host/+[^/]+/+[^/]+(/+[^/]+)?');
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
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());
if (match == null) {
throw InvalidURLError(name);
@ -47,17 +66,7 @@ class APKPure extends AppSource {
}
String? dateString =
html.querySelector('span.info-other span.date')?.text.trim();
DateTime? releaseDate;
try {
releaseDate = dateString != null
? DateFormat('MMM dd, yyyy').parse(dateString)
: null;
releaseDate = dateString != null && releaseDate == null
? DateFormat('MMMM dd, yyyy').parse(dateString)
: null;
} catch (err) {
// ignore
}
DateTime? releaseDate = parseDateTimeMMMddCommayyyy(dateString);
String type = html.querySelector('a.info-tag')?.text.trim() ?? 'APK';
List<MapEntry<String, String>> apkUrls = [
MapEntry('$appId.apk', 'https://d.$host/b/$type/$appId?version=latest')
@ -70,11 +79,13 @@ class APKPure extends AppSource {
Uri.parse(standardUrl).pathSegments.reversed.last;
String appName =
html.querySelector('h1.info-title')?.text.trim() ?? appId;
String? changeLog = htmlChangelog.querySelector("div.whats-new-info p:not(.date)")?.innerHtml
.trim().replaceAll("<br>", " \n");
String? changeLog = htmlChangelog
.querySelector("div.whats-new-info p:not(.date)")
?.innerHtml
.trim()
.replaceAll("<br>", " \n");
return APKDetails(version, apkUrls, AppNames(author, appName),
releaseDate: releaseDate,
changeLog: changeLog);
releaseDate: releaseDate, changeLog: changeLog);
} else {
throw getObtainiumHttpError(res);
}

View File

@ -1,8 +1,6 @@
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
@ -11,6 +9,7 @@ class Aptoide extends AppSource {
host = 'aptoide.com';
name = tr('Aptoide');
allowSubDomains = true;
naiveStandardVersionDetection = true;
}
@override
@ -26,14 +25,10 @@ class Aptoide extends AppSource {
@override
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async {
return (await getLatestAPKDetails(standardUrl, additionalSettings)).version;
return (await getAppDetailsJSON(standardUrl))['package'];
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
Future<Map<String, dynamic>> getAppDetailsJSON(String standardUrl) async {
var res = await sourceRequest(standardUrl);
if (res.statusCode != 200) {
throw getObtainiumHttpError(res);
@ -50,12 +45,20 @@ class Aptoide extends AppSource {
if (res2.statusCode != 200) {
throw getObtainiumHttpError(res);
}
var appDetails = jsonDecode(res2.body)?['nodes']?['meta']?['data'];
String appName = appDetails?['name'] ?? tr('app');
String author = appDetails?['developer']?['name'] ?? name;
String? dateStr = appDetails?['updated'];
String? version = appDetails?['file']?['vername'];
String? apkUrl = appDetails?['file']?['path'];
return jsonDecode(res2.body)?['nodes']?['meta']?['data'];
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
var appDetails = await getAppDetailsJSON(standardUrl);
String appName = appDetails['name'] ?? tr('app');
String author = appDetails['developer']?['name'] ?? name;
String? dateStr = appDetails['updated'];
String? version = appDetails['file']?['vername'];
String? apkUrl = appDetails['file']?['path'];
if (version == null) {
throw NoVersionError();
}
@ -71,34 +74,4 @@ class Aptoide extends AppSource {
version, getApkUrlsFromUrls([apkUrl]), AppNames(author, appName),
releaseDate: relDate);
}
@override
Future<Map<String, List<String>>> search(String query,
{Map<String, dynamic> querySettings = const {}}) async {
Response res = await sourceRequest(
'https://search.$host/?q=${Uri.encodeQueryComponent(query)}');
if (res.statusCode == 200) {
Map<String, List<String>> urlsWithDescriptions = {};
parse(res.body).querySelectorAll('.package-header').forEach((e) {
String? url = e.attributes['href'];
if (url != null) {
try {
standardizeUrl(url);
} catch (e) {
url = null;
}
}
if (url != null) {
urlsWithDescriptions[url] = [
e.querySelector('.package-name')?.text.trim() ?? '',
e.querySelector('.package-summary')?.text.trim() ??
tr('noDescription')
];
}
});
return urlsWithDescriptions;
} else {
throw getObtainiumHttpError(res);
}
}
}

View File

@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
@ -10,7 +11,28 @@ class FDroid extends AppSource {
FDroid() {
host = 'f-droid.org';
name = tr('fdroid');
naiveStandardVersionDetection = true;
canSearch = true;
additionalSourceAppSpecificSettingFormItems = [
[
GeneratedFormTextField('filterVersionsByRegEx',
label: tr('filterVersionsByRegEx'),
required: false,
additionalValidators: [
(value) {
return regExValidator(value);
}
])
],
[
GeneratedFormSwitch('trySelectingSuggestedVersionCode',
label: tr('trySelectingSuggestedVersionCode'))
],
[
GeneratedFormSwitch('autoSelectHighestVersionCode',
label: tr('autoSelectHighestVersionCode'))
],
];
}
@override
@ -36,28 +58,6 @@ class FDroid extends AppSource {
return Uri.parse(standardUrl).pathSegments.last;
}
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
Response res, String apkUrlPrefix, String standardUrl) {
if (res.statusCode == 200) {
List<dynamic> releases = jsonDecode(res.body)['packages'] ?? [];
if (releases.isEmpty) {
throw NoReleasesError();
}
String? latestVersion = releases[0]['versionName'];
if (latestVersion == null) {
throw NoVersionError();
}
List<String> apkUrls = releases
.where((element) => element['versionName'] == latestVersion)
.map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
.toList();
return APKDetails(latestVersion, getApkUrlsFromUrls(apkUrls),
AppNames(name, Uri.parse(standardUrl).pathSegments.last));
} else {
throw getObtainiumHttpError(res);
}
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
@ -68,7 +68,18 @@ class FDroid extends AppSource {
return getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest('https://$host/api/v1/packages/$appId'),
'https://$host/repo/$appId',
standardUrl);
standardUrl,
name,
autoSelectHighestVersionCode:
additionalSettings['autoSelectHighestVersionCode'] == true,
trySelectingSuggestedVersionCode:
additionalSettings['trySelectingSuggestedVersionCode'] == true,
filterVersionsByRegEx:
(additionalSettings['filterVersionsByRegEx'] as String?)
?.isNotEmpty ==
true
? additionalSettings['filterVersionsByRegEx']
: null);
}
@override
@ -101,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.toSet().toList()),
AppNames(sourceName, Uri.parse(standardUrl).pathSegments.last));
} else {
throw getObtainiumHttpError(res);
}
}

View File

@ -7,6 +7,9 @@ import 'package:obtainium/providers/source_provider.dart';
class FDroidRepo extends AppSource {
FDroidRepo() {
name = tr('fdroidThirdPartyRepo');
canSearch = true;
excludeFromMassSearch = true;
neverAutoSelect = true;
additionalSourceAppSpecificSettingFormItems = [
[
@ -22,17 +25,91 @@ 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
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
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'];
if (appIdOrName == null) {
throw NoReleasesError();
}
var res = await sourceRequest('$standardUrl/index.xml');
var res = await sourceRequest(
'$standardUrl${standardUrl.endsWith('/index.xml') ? '' : '/index.xml'}');
if (res.statusCode == 200) {
var body = parse(res.body);
var foundApps = body.querySelectorAll('application').where((element) {
@ -41,7 +118,7 @@ class FDroidRepo extends AppSource {
if (foundApps.isEmpty) {
foundApps = body.querySelectorAll('application').where((element) {
return element.querySelector('name')?.innerHtml.toLowerCase() ==
appIdOrName.toLowerCase();
appIdOrName!.toLowerCase();
}).toList();
}
if (foundApps.isEmpty) {
@ -50,7 +127,7 @@ class FDroidRepo extends AppSource {
.querySelector('name')
?.innerHtml
.toLowerCase()
.contains(appIdOrName.toLowerCase()) ??
.contains(appIdOrName!.toLowerCase()) ??
false;
}).toList();
}
@ -58,8 +135,9 @@ class FDroidRepo extends AppSource {
throw ObtainiumError(tr('appWithIdOrNameNotFound'));
}
var authorName = body.querySelector('repo')?.attributes['name'] ?? name;
var appName =
foundApps[0].querySelector('name')?.innerHtml ?? appIdOrName;
String appId = foundApps[0].attributes['id']!;
foundApps[0].querySelector('name')?.innerHtml ?? appId;
var appName = foundApps[0].querySelector('name')?.innerHtml ?? appId;
var releases = foundApps[0].querySelectorAll('package');
String? latestVersion = releases[0].querySelector('version')?.innerHtml;
String? added = releases[0].querySelector('added')?.innerHtml;

View File

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
@ -21,21 +22,6 @@ class GitHub extends AppSource {
label: tr('githubPATLabel'),
password: true,
required: false,
additionalValidators: [
(value) {
if (value != null && value.trim().isNotEmpty) {
if (value
.split(':')
.where((element) => element.trim().isNotEmpty)
.length !=
2) {
return tr('githubPATHint');
}
}
return null;
}
],
hint: tr('githubPATFormat'),
belowWidgets: [
const SizedBox(
height: 4,
@ -86,9 +72,10 @@ class GitHub extends AppSource {
}
])
],
[GeneratedFormSwitch('verifyLatestTag', label: tr('verifyLatestTag'))],
[
GeneratedFormSwitch('verifyLatestTag',
label: tr('verifyLatestTag'), defaultValue: false)
GeneratedFormSwitch('dontSortReleasesList',
label: tr('dontSortReleasesList'))
]
];
@ -169,26 +156,53 @@ class GitHub extends AppSource {
return url.substring(0, match.end);
}
Future<String> getCredentialPrefixIfAny(
Map<String, dynamic> additionalSettings) async {
@override
Future<Map<String, String>?> getRequestHeaders(
{Map<String, dynamic> additionalSettings = const <String, dynamic>{},
bool forAPKDownload = false}) async {
var token = await getTokenIfAny(additionalSettings);
var headers = <String, String>{};
if (token != null) {
headers[HttpHeaders.authorizationHeader] = 'Token $token';
}
if (forAPKDownload == true) {
headers[HttpHeaders.acceptHeader] = 'application/octet-stream';
}
if (headers.isNotEmpty) {
return headers;
} else {
return null;
}
}
Future<String?> getTokenIfAny(Map<String, dynamic> additionalSettings) async {
SettingsProvider settingsProvider = SettingsProvider();
await settingsProvider.initializeSettings();
var sourceConfig =
await getSourceConfigValues(additionalSettings, settingsProvider);
String? creds = sourceConfig['github-creds'];
return creds != null && creds.isNotEmpty ? '$creds@' : '';
if (creds != null) {
var userNameEndIndex = creds.indexOf(':');
if (userNameEndIndex > 0) {
creds = creds.substring(
userNameEndIndex + 1); // For old username-included token inputs
}
return creds;
} else {
return null;
}
}
@override
Future<String?> getSourceNote() async {
if (!hostChanged && (await getCredentialPrefixIfAny({})).isEmpty) {
if (!hostChanged && (await getTokenIfAny({})) == null) {
return '${tr('githubSourceNote')} ${hostChanged ? tr('addInfoBelow') : tr('addInfoInSettings')}';
}
return null;
}
Future<String> getAPIHost(Map<String, dynamic> additionalSettings) async =>
'https://${await getCredentialPrefixIfAny(additionalSettings)}api.$host';
'https://api.$host';
Future<String> convertStandardUrlToAPIUrl(
String standardUrl, Map<String, dynamic> additionalSettings) async =>
@ -217,6 +231,8 @@ class GitHub extends AppSource {
? additionalSettings['filterReleaseNotesByRegEx']
: null;
bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true;
bool dontSortReleasesList =
additionalSettings['dontSortReleasesList'] == true;
String? latestTag;
if (verifyLatestTag) {
var temp = requestUrl.split('?');
@ -238,9 +254,10 @@ class GitHub extends AppSource {
List<MapEntry<String, String>> getReleaseAPKUrls(dynamic release) =>
(release['assets'] as List<dynamic>?)
?.map((e) {
return e['name'] != null && e['browser_download_url'] != null
return (e['name'] != null) &&
((e['url'] ?? e['browser_download_url']) != null)
? MapEntry(e['name'] as String,
e['browser_download_url'] as String)
(e['url'] ?? e['browser_download_url']) as String)
: const MapEntry('', '');
})
.where((element) => element.key.toLowerCase().endsWith('.apk'))
@ -251,32 +268,36 @@ class GitHub extends AppSource {
rel?['published_at'] != null
? DateTime.parse(rel['published_at'])
: null;
releases.sort((a, b) {
// See #478 and #534
if (a == b) {
return 0;
} else if (a == null) {
return -1;
} else if (b == null) {
return 1;
} else {
var nameA = a['tag_name'] ?? a['name'];
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));
if (dontSortReleasesList) {
releases = releases.reversed.toList();
} else {
releases.sort((a, b) {
// See #478 and #534
if (a == b) {
return 0;
} else if (a == null) {
return -1;
} else if (b == null) {
return 1;
} else {
return (getReleaseDateFromRelease(a) ?? DateTime(1))
.compareTo(getReleaseDateFromRelease(b) ?? DateTime(0));
var nameA = a['tag_name'] ?? a['name'];
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 &&
releases.isNotEmpty &&
latestTag !=

View File

@ -1,4 +1,5 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/components/generated_form.dart';
@ -18,10 +19,10 @@ String ensureAbsoluteUrl(String ambiguousUrl, Uri referenceAbsoluteUrl) {
.toList();
if (ambiguousUrl.startsWith('/') || currPathSegments.isEmpty) {
return '${referenceAbsoluteUrl.origin}/$ambiguousUrl';
} else if (ambiguousUrl.split('/').length == 1) {
} else if (ambiguousUrl.split('/').where((e) => e.isNotEmpty).length == 1) {
return '${referenceAbsoluteUrl.origin}/${currPathSegments.join('/')}/$ambiguousUrl';
} else {
return '${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - 1).join('/')}/$ambiguousUrl';
return '${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - (currPathSegments.last.contains('.') ? 1 : 0)).join('/')}/$ambiguousUrl';
}
}
@ -92,6 +93,7 @@ class HTML extends AppSource {
GeneratedFormSwitch('sortByFileNamesNotLinks',
label: tr('sortByFileNamesNotLinks'))
],
[GeneratedFormSwitch('reverseSort', label: tr('reverseSort'))],
[
GeneratedFormTextField('customLinkFilterRegex',
label: tr('customLinkFilterRegex'),
@ -106,9 +108,35 @@ class HTML extends AppSource {
[
GeneratedFormTextField('intermediateLinkRegex',
label: tr('intermediateLinkRegex'),
hint: '([0-9]+\.)*[0-9]+/\$',
hint: '([0-9]+.)*[0-9]+/\$',
required: false,
additionalValidators: [(value) => regExValidator(value)])
],
[
GeneratedFormTextField('versionExtractionRegEx',
label: tr('versionExtractionRegEx'),
required: false,
additionalValidators: [(value) => regExValidator(value)]),
],
[
GeneratedFormTextField('matchGroupToUse',
label: tr('matchGroupToUse'),
required: false,
hint: '0',
textInputType: const TextInputType.numberWithOptions(),
additionalValidators: [
(value) {
if (value?.isEmpty == true) {
value = null;
}
value ??= '0';
return intValidator(value);
}
])
],
[
GeneratedFormSwitch('versionExtractWholePage',
label: tr('versionExtractWholePage'))
]
];
overrideVersionDetectionFormDefault('noVersionDetection',
@ -116,11 +144,14 @@ class HTML extends AppSource {
}
@override
// TODO: implement requestHeaders choice, hardcoded for now
Map<String, String>? get requestHeaders => {
"User-Agent":
"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36"
};
Future<Map<String, String>?> getRequestHeaders(
{Map<String, dynamic> additionalSettings = const <String, dynamic>{},
bool forAPKDownload = false}) async {
return {
"User-Agent":
"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36"
};
}
@override
String sourceSpecificStandardizeURL(String url) {
@ -139,7 +170,15 @@ class HTML extends AppSource {
List<String> allLinks = html
.querySelectorAll('a')
.map((element) => element.attributes['href'] ?? '')
.where((element) => element.isNotEmpty)
.toList();
if (allLinks.isEmpty) {
allLinks = RegExp(
r'(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?')
.allMatches(res.body)
.map((match) => match.group(0)!)
.toList();
}
List<String> links = [];
if ((additionalSettings['intermediateLinkRegex'] as String?)
?.isNotEmpty ==
@ -171,6 +210,9 @@ class HTML extends AppSource {
? compareAlphaNumeric(a.split('/').where((e) => e.isNotEmpty).last,
b.split('/').where((e) => e.isNotEmpty).last)
: compareAlphaNumeric(a, b));
if (additionalSettings['reverseSort'] == true) {
links = links.reversed.toList();
}
if ((additionalSettings['apkFilterRegEx'] as String?)?.isNotEmpty ==
true) {
var reg = RegExp(additionalSettings['apkFilterRegEx']);
@ -180,10 +222,30 @@ class HTML extends AppSource {
throw NoReleasesError();
}
var rel = links.last;
var version = rel.hashCode.toString();
String? version = rel.hashCode.toString();
var versionExtractionRegEx =
additionalSettings['versionExtractionRegEx'] as String?;
if (versionExtractionRegEx?.isNotEmpty == true) {
var match = RegExp(versionExtractionRegEx!).allMatches(
additionalSettings['versionExtractWholePage'] == true
? res.body.split('\r\n').join('\n').split('\n').join('\\n')
: rel);
if (match.isEmpty) {
throw NoVersionError();
}
String matchGroupString =
(additionalSettings['matchGroupToUse'] as String).trim();
if (matchGroupString.isEmpty) {
matchGroupString = "0";
}
version = match.last.group(int.parse(matchGroupString));
if (version?.isEmpty == true) {
throw NoVersionError();
}
}
List<String> apkUrls =
[rel].map((e) => ensureAbsoluteUrl(e, uri)).toList();
return APKDetails(version, apkUrls.map((e) => MapEntry(e, e)).toList(),
return APKDetails(version!, apkUrls.map((e) => MapEntry(e, e)).toList(),
AppNames(uri.host, tr('app')));
} else {
throw getObtainiumHttpError(res);

View File

@ -3,14 +3,25 @@ import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
class IzzyOnDroid extends AppSource {
late FDroid fd;
IzzyOnDroid() {
host = 'android.izzysoft.de';
host = 'izzysoft.de';
fd = FDroid();
additionalSourceAppSpecificSettingFormItems =
fd.additionalSourceAppSpecificSettingFormItems;
allowSubDomains = true;
}
@override
String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://$host/repo/apk/[^/]+');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
RegExp standardUrlRegExA = RegExp('^https?://android.$host/repo/apk/[^/]+');
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) {
throw InvalidURLError(name);
}
@ -20,7 +31,7 @@ class IzzyOnDroid extends AppSource {
@override
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async {
return FDroid().tryInferringAppId(standardUrl);
return fd.tryInferringAppId(standardUrl);
}
@override
@ -29,10 +40,16 @@ class IzzyOnDroid extends AppSource {
Map<String, dynamic> additionalSettings,
) async {
String? appId = await tryInferringAppId(standardUrl);
return FDroid().getAPKUrlsFromFDroidPackagesAPIResponse(
return getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest(
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),
'https://android.izzysoft.de/frepo/$appId',
standardUrl);
standardUrl,
name,
autoSelectHighestVersionCode:
additionalSettings['autoSelectHighestVersionCode'] == true,
trySelectingSuggestedVersionCode:
additionalSettings['trySelectingSuggestedVersionCode'] == true,
filterVersionsByRegEx: additionalSettings['filterVersionsByRegEx']);
}
}

View File

@ -0,0 +1,99 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart';
import 'package:obtainium/app_sources/apkpure.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
class Uptodown extends AppSource {
Uptodown() {
host = 'uptodown.com';
allowSubDomains = true;
naiveStandardVersionDetection = true;
}
@override
String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://([^\\.]+\\.){2,}$host');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) {
throw InvalidURLError(name);
}
return '${url.substring(0, match.end)}/android/download';
}
@override
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async {
return (await getAppDetailsFromPage(standardUrl))['appId'];
}
Future<Map<String, String?>> getAppDetailsFromPage(String standardUrl) async {
var res = await sourceRequest(standardUrl);
if (res.statusCode != 200) {
throw getObtainiumHttpError(res);
}
var html = parse(res.body);
String? version = html.querySelector('div.version')?.innerHtml;
String? apkUrl =
'${standardUrl.split('/').reversed.toList().sublist(1).reversed.join('/')}/post-download';
String? name = html.querySelector('#detail-app-name')?.innerHtml.trim();
String? author = html.querySelector('#author-link')?.innerHtml.trim();
var detailElements = html.querySelectorAll('#technical-information td');
String? appId = (detailElements.elementAtOrNull(2))?.innerHtml.trim();
String? dateStr = (detailElements.elementAtOrNull(29))?.innerHtml.trim();
return Map.fromEntries([
MapEntry('version', version),
MapEntry('apkUrl', apkUrl),
MapEntry('appId', appId),
MapEntry('name', name),
MapEntry('author', author),
MapEntry('dateStr', dateStr)
]);
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
var appDetails = await getAppDetailsFromPage(standardUrl);
var version = appDetails['version'];
var apkUrl = appDetails['apkUrl'];
var appId = appDetails['appId'];
if (version == null) {
throw NoVersionError();
}
if (apkUrl == null) {
throw NoAPKError();
}
if (appId == null) {
throw NoReleasesError();
}
String appName = appDetails['name'] ?? tr('app');
String author = appDetails['author'] ?? name;
String? dateStr = appDetails['dateStr'];
DateTime? relDate;
if (dateStr != null) {
relDate = parseDateTimeMMMddCommayyyy(dateStr);
}
return APKDetails(
version, getApkUrlsFromUrls([apkUrl]), AppNames(author, appName),
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 finalUrlKey =
html.querySelector('.post-download')?.attributes['data-url'];
if (finalUrlKey == null) {
throw NoAPKError();
}
return 'https://dw.$host/dwn/$finalUrlKey';
}
}

View File

@ -12,7 +12,12 @@ class VLC extends AppSource {
get dwUrlBase => 'https://get.$host/vlc-android/';
@override
Map<String, String>? get requestHeaders => HTML().requestHeaders;
Future<Map<String, String>?> getRequestHeaders(
{Map<String, dynamic> additionalSettings = const <String, dynamic>{},
bool forAPKDownload = false}) =>
HTML().getRequestHeaders(
additionalSettings: additionalSettings,
forAPKDownload: forAPKDownload);
@override
String sourceSpecificStandardizeURL(String url) {

View File

@ -16,14 +16,13 @@ class WhatsApp extends AppSource {
@override
Future<String> apkUrlPrefetchModifier(
String apkUrl, String standardUrl) async {
Response res = await sourceRequest('https://www.whatsapp.com/android');
Response res = await sourceRequest('$standardUrl/android');
if (res.statusCode == 200) {
var targetLinks = parse(res.body)
.querySelectorAll('a')
.map((e) => e.attributes['href'] ?? '')
.where((e) => e.isNotEmpty)
.where((e) =>
e.contains('content.whatsapp.net') && e.contains('WhatsApp.apk'))
.where((e) => e.contains('WhatsApp.apk'))
.toList();
if (targetLinks.isEmpty) {
throw NoAPKError();
@ -39,37 +38,16 @@ class WhatsApp extends AppSource {
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
Response res = await sourceRequest('https://www.whatsapp.com/android');
if (res.statusCode == 200) {
var targetElements = parse(res.body)
.querySelectorAll('p')
.where((element) => element.innerHtml.contains('Version '))
.toList();
if (targetElements.isEmpty) {
throw NoVersionError();
}
var vLines = targetElements[0]
.innerHtml
.split('\n')
.where((element) => element.contains('Version '))
.toList();
if (vLines.isEmpty) {
throw NoVersionError();
}
var versionMatch = RegExp('[0-9]+(\\.[0-9]+)+').firstMatch(vLines[0]);
if (versionMatch == null) {
throw NoVersionError();
}
String version =
vLines[0].substring(versionMatch.start, versionMatch.end);
return APKDetails(
version,
getApkUrlsFromUrls([
'https://www.whatsapp.com/android?v=$version&=thisIsaPlaceholder&a=realURLPrefetchedAtDownloadTime'
]),
AppNames('Meta', 'WhatsApp'));
} else {
throw getObtainiumHttpError(res);
}
// This is a CDN link that is consistent per version
// But it has query params that change constantly
Uri apkUri =
Uri.parse(await apkUrlPrefetchModifier(standardUrl, standardUrl));
var unusableApkUrl = '${apkUri.origin}/${apkUri.path}';
// So we use the param-less URL is a pseudo-version to add the app and check for updates
// See #357 for why we can't scrape the version number directly
// But we re-fetch the URL again with its latest query params at the actual download time
String version = unusableApkUrl.hashCode.toString();
return APKDetails(version, getApkUrlsFromUrls([unusableApkUrl]),
AppNames('Meta', 'WhatsApp'));
}
}

View File

@ -25,6 +25,7 @@ class GeneratedFormTextField extends GeneratedFormItem {
late int max;
late String? hint;
late bool password;
late TextInputType? textInputType;
GeneratedFormTextField(String key,
{String label = 'Input',
@ -34,7 +35,8 @@ class GeneratedFormTextField extends GeneratedFormItem {
this.required = true,
this.max = 1,
this.hint,
this.password = false})
this.password = false,
this.textInputType})
: super(key,
label: label,
belowWidgets: belowWidgets,
@ -144,7 +146,8 @@ Color generateRandomLightColor() {
// Map from HPLuv color space to RGB, use constant saturation=100, lightness=70
final List<double> rgbValuesDbl = Hsluv.hpluvToRgb([hue, 100, 70]);
// Map RBG values from 0-1 to 0-255:
final List<int> rgbValues = rgbValuesDbl.map((rgb) => (rgb * 255).toInt()).toList();
final List<int> rgbValues =
rgbValuesDbl.map((rgb) => (rgb * 255).toInt()).toList();
return Color.fromARGB(255, rgbValues[0], rgbValues[1], rgbValues[2]);
}
@ -190,6 +193,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
if (formItem is GeneratedFormTextField) {
final formFieldKey = GlobalKey<FormFieldState>();
return TextFormField(
keyboardType: formItem.textInputType,
obscureText: formItem.password,
autocorrect: !formItem.password,
enableSuggestions: !formItem.password,
@ -370,34 +374,37 @@ class _GeneratedFormState extends State<GeneratedForm> {
}) ??
[const SizedBox.shrink()],
(values[widget.items[r][e].key]
as Map<String, MapEntry<int, bool>>?)
?.values
.where((e) => e.value)
.length == 1
as Map<String, MapEntry<int, bool>>?)
?.values
.where((e) => e.value)
.length ==
1
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: IconButton(
onPressed: () {
setState(() {
var temp = values[widget.items[r][e].key]
as Map<String, MapEntry<int, bool>>;
// get selected category str where bool is true
final oldEntry = temp.entries.firstWhere((entry) => entry.value.value);
// generate new color, ensure it is not the same
int newColor = oldEntry.value.key;
while(oldEntry.value.key == newColor) {
newColor = generateRandomLightColor().value;
}
// Update entry with new color, remain selected
temp.update(oldEntry.key, (old) => MapEntry(newColor, old.value));
values[widget.items[r][e].key] = temp;
someValueChanged();
});
},
icon: const Icon(Icons.format_color_fill_rounded),
visualDensity: VisualDensity.compact,
tooltip: tr('colour'),
))
padding: const EdgeInsets.symmetric(horizontal: 4),
child: IconButton(
onPressed: () {
setState(() {
var temp = values[widget.items[r][e].key]
as Map<String, MapEntry<int, bool>>;
// get selected category str where bool is true
final oldEntry = temp.entries
.firstWhere((entry) => entry.value.value);
// generate new color, ensure it is not the same
int newColor = oldEntry.value.key;
while (oldEntry.value.key == newColor) {
newColor = generateRandomLightColor().value;
}
// Update entry with new color, remain selected
temp.update(oldEntry.key,
(old) => MapEntry(newColor, old.value));
values[widget.items[r][e].key] = temp;
someValueChanged();
});
},
icon: const Icon(Icons.format_color_fill_rounded),
visualDensity: VisualDensity.compact,
tooltip: tr('colour'),
))
: const SizedBox.shrink(),
(values[widget.items[r][e].key]
as Map<String, MapEntry<int, bool>>?)

View File

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:android_package_installer/android_package_installer.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@ -65,30 +67,43 @@ class NotImplementedError 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);
add(String appId, String string) {
var tempIds = content.remove(string);
add(String appId, dynamic error, {String? appName}) {
if (error is SocketException) {
error = error.message;
}
rawErrors[appId] = error;
var string = error.toString();
var tempIds = idsByErrorString.remove(string);
tempIds ??= [];
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
String toString() {
String finalString = '';
for (var e in content.keys) {
finalString += '$e: ${content[e].toString()}\n\n';
}
return finalString;
}
String toString() => idsByErrorString.entries
.map((e) => errorsAppsString(e.key, e.value))
.join('\n\n');
}
showError(dynamic e, BuildContext context) {
showMessage(dynamic e, BuildContext context, {bool isError = 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)) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
@ -100,8 +115,8 @@ showError(dynamic e, BuildContext context) {
return AlertDialog(
scrollable: true,
title: Text(e is MultiAppMultiError
? tr('someErrors')
: tr('unexpectedError')),
? tr(isError ? 'someErrors' : 'updates')
: tr(isError ? 'unexpectedError' : 'unknown')),
content: GestureDetector(
onLongPress: () {
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) {
return list.length == 2
? '${list[0]} ${tr('and')} ${list[1]}'

View File

@ -19,7 +19,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
// ignore: implementation_imports
import 'package:easy_localization/src/localization.dart';
const String currentVersion = '0.14.7';
const String currentVersion = '0.14.32';
const String currentReleaseTag =
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
@ -27,7 +27,7 @@ const int bgUpdateCheckAlarmId = 666;
List<MapEntry<Locale, String>> supportedLocales = const [
MapEntry(Locale('en'), 'English'),
MapEntry(Locale('zh'), '汉语'),
MapEntry(Locale('zh'), '简体中文'),
MapEntry(Locale('it'), 'Italiano'),
MapEntry(Locale('ja'), '日本語'),
MapEntry(Locale('hu'), 'Magyar'),
@ -38,7 +38,10 @@ List<MapEntry<Locale, String>> supportedLocales = const [
MapEntry(Locale('pl'), 'Polski'),
MapEntry(Locale('ru'), 'Русский язык'),
MapEntry(Locale('bs'), 'Bosanski'),
// MapEntry(Locale('br'), 'Brasileiro'),
MapEntry(Locale('pt'), 'Brasileiro'),
MapEntry(Locale('cs'), 'Česky'),
MapEntry(Locale('sv'), 'Svenska'),
MapEntry(Locale('nl'), 'Nederlands'),
];
const fallbackLocale = Locale('en');
const localeDir = 'assets/translations';

View File

@ -15,8 +15,10 @@ class GitHubStars implements MassAppUrlSource {
Future<Map<String, List<String>>> getOnePageOfUserStarredUrlsWithDescriptions(
String username, int page) async {
Response res = await get(Uri.parse(
'https://${await GitHub().getCredentialPrefixIfAny({})}api.github.com/users/$username/starred?per_page=100&page=$page'));
Response res = await get(
Uri.parse(
'https://api.github.com/users/$username/starred?per_page=100&page=$page'),
headers: await GitHub().getRequestHeaders());
if (res.statusCode == 200) {
Map<String, List<String>> urlsWithDescriptions = {};
for (var e in (jsonDecode(res.body) as List<dynamic>)) {

View File

@ -11,6 +11,7 @@ import 'package:obtainium/pages/app.dart';
import 'package:obtainium/pages/import_export.dart';
import 'package:obtainium/pages/settings.dart';
import 'package:obtainium/providers/apps_provider.dart';
import 'package:obtainium/providers/notifications_provider.dart';
import 'package:obtainium/providers/settings_provider.dart';
import 'package:obtainium/providers/source_provider.dart';
import 'package:provider/provider.dart';
@ -42,6 +43,8 @@ class _AddAppPageState extends State<AddAppPage> {
Widget build(BuildContext context) {
AppsProvider appsProvider = context.read<AppsProvider>();
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
NotificationsProvider notificationsProvider =
context.read<NotificationsProvider>();
bool doingSomething = gettingAppInfo || searching;
@ -145,13 +148,12 @@ class _AddAppPageState extends State<AddAppPage> {
userPickedTrackOnly))) {
var trackOnly = pickedSource!.enforceTrackOnly || userPickedTrackOnly;
app = await sourceProvider.getApp(
pickedSource!, userInput, additionalSettings,
pickedSource!, userInput.trim(), additionalSettings,
trackOnlyOverride: trackOnly,
overrideSource: pickedSourceOverride,
inferAppIdIfOptional: inferAppIdIfOptional);
// Only download the APK here if you need to for the package ID
if (sourceProvider.isTempId(app) &&
app.additionalSettings['trackOnly'] != true) {
if (isTempId(app) && app.additionalSettings['trackOnly'] != true) {
// ignore: use_build_context_synchronously
var apkUrl = await appsProvider.confirmApkUrl(app, context);
if (apkUrl == null) {
@ -161,7 +163,8 @@ class _AddAppPageState extends State<AddAppPage> {
app.apkUrls.map((e) => e.value).toList().indexOf(apkUrl.value);
// ignore: use_build_context_synchronously
var downloadedArtifact = await appsProvider.downloadApp(
app, globalNavigatorKey.currentContext);
app, globalNavigatorKey.currentContext,
notificationsProvider: notificationsProvider);
DownloadedApk? downloadedFile;
DownloadedXApkDir? downloadedDir;
if (downloadedArtifact is DownloadedApk) {
@ -256,8 +259,9 @@ class _AddAppPageState extends State<AddAppPage> {
searching = true;
});
try {
var results = await Future.wait(
sourceProvider.sources.where((e) => e.canSearch).map((e) async {
var results = await Future.wait(sourceProvider.sources
.where((e) => e.canSearch && !e.excludeFromMassSearch)
.map((e) async {
try {
return await e.search(searchQuery);
} catch (err) {
@ -459,14 +463,12 @@ class _AddAppPageState extends State<AddAppPage> {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 48,
),
Text(
tr('supportedSourcesBelow'),
tr('supportedSources'),
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(
height: 8,
height: 16,
),
...sourceProvider.sources
.map((e) => GestureDetector(
@ -520,15 +522,17 @@ class _AddAppPageState extends State<AddAppPage> {
: const SizedBox();
},
future: pickedSource?.getSourceNote()),
const SizedBox(
height: 16,
SizedBox(
height: pickedSource != null ? 16 : 96,
),
if (pickedSource != null)
getAdditionalOptsCol()
else
getSourcesListWidget(),
const SizedBox(
height: 8,
if (pickedSource != null) getAdditionalOptsCol(),
if (pickedSource == null)
const Divider(
height: 48,
),
if (pickedSource == null) getSourcesListWidget(),
SizedBox(
height: pickedSource != null ? 8 : 2,
),
])),
)

View File

@ -152,13 +152,16 @@ class _AppPageState extends State<AppPage> {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 125),
const SizedBox(height: 20),
app?.icon != null
? Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Image.memory(
app!.icon!,
height: 150,
gaplessPlayback: true,
GestureDetector(
child: Image.memory(
app!.icon!,
height: 150,
gaplessPlayback: true,
),
onTap: () => pm.openApp(app.app.id),
)
])
: Container(),
@ -292,7 +295,7 @@ class _AppPageState extends State<AppPage> {
if (source?.enforceTrackOnly == true) {
app.app.additionalSettings['trackOnly'] = true;
// ignore: use_build_context_synchronously
showError(tr('appsFromSourceAreTrackOnly'), context);
showMessage(tr('appsFromSourceAreTrackOnly'), context);
}
if (app.app.additionalSettings['versionDetection'] ==
'releaseDateAsVersion') {
@ -338,12 +341,12 @@ class _AppPageState extends State<AppPage> {
try {
HapticFeedback.heavyImpact();
var res = await appsProvider.downloadAndInstallLatestApps(
app?.app.id != null ? [app!.app.id] : [],
globalNavigatorKey.currentContext,
settingsProvider);
app?.app.id != null ? [app!.app.id] : [],
globalNavigatorKey.currentContext,
);
if (app?.app.installedVersion != null && !trackOnly) {
// ignore: use_build_context_synchronously
showError(tr('appsUpdated'), context);
showMessage(tr('appsUpdated'), context);
}
if (res.isNotEmpty && mounted) {
Navigator.of(context).pop();
@ -464,8 +467,17 @@ class _AppPageState extends State<AppPage> {
],
));
appScreenAppBar() => AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.pop(context);
},
),
);
return Scaffold(
appBar: settingsProvider.showAppWebpage ? AppBar() : null,
appBar: settingsProvider.showAppWebpage ? AppBar() : appScreenAppBar(),
backgroundColor: Theme.of(context).colorScheme.surface,
body: RefreshIndicator(
child: settingsProvider.showAppWebpage

View File

@ -68,7 +68,7 @@ class AppsPageState extends State<AppsPage> {
refreshingSince = DateTime.now();
});
return appsProvider.checkUpdates().catchError((e) {
showError(e, context);
showError(e is Map ? e['errors'] : e, context);
return <App>[];
}).whenComplete(() {
setState(() {
@ -381,8 +381,7 @@ class AppsPageState extends State<AppsPage> {
: () {
appsProvider.downloadAndInstallLatestApps(
[listedApps[appIndex].app.id],
globalNavigatorKey.currentContext,
settingsProvider).catchError((e) {
globalNavigatorKey.currentContext).catchError((e) {
showError(e, context);
return <String>[];
});
@ -449,33 +448,48 @@ class AppsPageState extends State<AppsPage> {
: const SizedBox.shrink(),
GestureDetector(
onTap: showChangesFn,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Row(mainAxisSize: MainAxisSize.min, children: [
Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width / 4),
child: Text(getVersionText(index),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.end)),
]),
Row(
mainAxisSize: MainAxisSize.min,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: settingsProvider.highlightTouchTargets &&
showChangesFn != null
? (Theme.of(context).brightness == Brightness.light
? Theme.of(context).primaryColor
: Theme.of(context).primaryColorLight)
.withAlpha(20)
: null),
padding: settingsProvider.highlightTouchTargets
? const EdgeInsetsDirectional.fromSTEB(12, 0, 12, 0)
: const EdgeInsetsDirectional.fromSTEB(24, 0, 0, 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
getChangesButtonString(index, showChangesFn != null),
style: TextStyle(
fontStyle: FontStyle.italic,
decoration: showChangesFn != null
? TextDecoration.underline
: TextDecoration.none),
)
Row(mainAxisSize: MainAxisSize.min, children: [
Container(
constraints: BoxConstraints(
maxWidth:
MediaQuery.of(context).size.width / 4),
child: Text(getVersionText(index),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.end)),
]),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
getChangesButtonString(
index, showChangesFn != null),
style: TextStyle(
fontStyle: FontStyle.italic,
decoration: showChangesFn != null
? TextDecoration.underline
: TextDecoration.none),
)
],
),
],
),
],
))
)))
],
);
@ -552,7 +566,7 @@ class AppsPageState extends State<AppsPage> {
.toInt()
.toString()
])
: tr('pleaseWait'),
: tr('installing'),
textAlign: (listedApps[index].downloadProgress! >= 0)
? TextAlign.start
: TextAlign.end,
@ -684,14 +698,14 @@ class AppsPageState extends State<AppsPage> {
toInstall.addAll(trackOnlyUpdateIdsAllOrSelected);
}
appsProvider
.downloadAndInstallLatestApps(toInstall,
globalNavigatorKey.currentContext, settingsProvider)
.downloadAndInstallLatestApps(
toInstall, globalNavigatorKey.currentContext)
.catchError((e) {
showError(e, context);
return <String>[];
}).then((value) {
if (shouldInstallUpdates) {
showError(tr('appsUpdated'), context);
showMessage(tr('appsUpdated'), context);
}
});
}
@ -819,7 +833,7 @@ class AppsPageState extends State<AppsPage> {
items: const [],
initValid: true,
message: tr('installStatusOfXWillBeResetExplanation',
args: [plural('app', selectedAppIds.length)]),
args: [plural('apps', selectedAppIds.length)]),
);
});
if (values != null) {

View File

@ -28,8 +28,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
@override
Widget build(BuildContext context) {
SourceProvider sourceProvider = SourceProvider();
var appsProvider = context.read<AppsProvider>();
var settingsProvider = context.read<SettingsProvider>();
var appsProvider = context.watch<AppsProvider>();
var settingsProvider = context.watch<SettingsProvider>();
var outlineButtonStyle = ButtonStyle(
shape: MaterialStateProperty.all(
@ -81,7 +81,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
});
appsProvider.addAppsByURL(urls).then((errors) {
if (errors.isEmpty) {
showError(tr('importedX', args: [plural('apps', urls.length)]),
showMessage(tr('importedX', args: [plural('apps', urls.length)]),
context);
} else {
showDialog(
@ -102,10 +102,17 @@ class _ImportExportPageState extends State<ImportExportPage> {
});
}
runObtainiumExport() {
runObtainiumExport({bool pickOnly = false}) async {
HapticFeedback.selectionClick();
appsProvider.exportApps().then((String path) {
showError(tr('exportedTo', args: [path]), context);
appsProvider
.exportApps(
pickOnly:
pickOnly || (await settingsProvider.getExportDir()) == null,
sp: settingsProvider)
.then((String? result) {
if (result != null) {
showMessage(tr('exportedTo', args: [result]), context);
}
}).catchError((e) {
showError(e, context);
});
@ -134,7 +141,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
}
});
appsProvider.addMissingCategories(settingsProvider);
showError(tr('importedX', args: [plural('apps', value)]), context);
showMessage(
tr('importedX', args: [plural('apps', value)]), context);
});
} else {
// User canceled the picker
@ -209,8 +217,9 @@ class _ImportExportPageState extends State<ImportExportPage> {
var errors = await appsProvider.addAppsByURL(selectedUrls);
if (errors.isEmpty) {
// ignore: use_build_context_synchronously
showError(
tr('importedX', args: [plural('app', selectedUrls.length)]),
showMessage(
tr('importedX',
args: [plural('apps', selectedUrls.length)]),
context);
} else {
// ignore: use_build_context_synchronously
@ -266,8 +275,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
var errors = await appsProvider.addAppsByURL(selectedUrls);
if (errors.isEmpty) {
// ignore: use_build_context_synchronously
showError(
tr('importedX', args: [plural('app', selectedUrls.length)]),
showMessage(
tr('importedX', args: [plural('apps', selectedUrls.length)]),
context);
} else {
// ignore: use_build_context_synchronously
@ -301,27 +310,85 @@ class _ImportExportPageState extends State<ImportExportPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Expanded(
child: TextButton(
style: outlineButtonStyle,
onPressed: appsProvider.apps.isEmpty ||
importInProgress
? null
: runObtainiumExport,
child: Text(tr('obtainiumExport')))),
const SizedBox(
width: 16,
),
Expanded(
child: TextButton(
style: outlineButtonStyle,
onPressed: importInProgress
? null
: runObtainiumImport,
child: Text(tr('obtainiumImport'))))
],
FutureBuilder(
future: settingsProvider.getExportDir(),
builder: (context, snapshot) {
return Column(
children: [
Row(
children: [
Expanded(
child: TextButton(
style: outlineButtonStyle,
onPressed: appsProvider.apps.isEmpty ||
importInProgress
? null
: () {
runObtainiumExport(pickOnly: true);
},
child: Text(tr('pickExportDir')),
)),
const SizedBox(
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(
child: TextButton(
style: outlineButtonStyle,
onPressed: importInProgress
? null
: runObtainiumImport,
child: Text(tr('obtainiumImport')))),
],
),
if (snapshot.data != null)
Column(
children: [
const SizedBox(height: 16),
GeneratedForm(
items: [
[
GeneratedFormSwitch(
'autoExportOnChanges',
label: tr('autoExportOnChanges'),
defaultValue: settingsProvider
.autoExportOnChanges,
)
]
],
onValueChanges:
(value, valid, isBuilding) {
if (valid && !isBuilding) {
if (value['autoExportOnChanges'] !=
null) {
settingsProvider
.autoExportOnChanges = value[
'autoExportOnChanges'] ==
true;
}
}
}),
],
),
],
);
},
),
if (importInProgress)
const Column(
@ -399,7 +466,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
fontStyle: FontStyle.italic, fontSize: 12)),
const SizedBox(
height: 8,
)
),
],
)))
]));

View File

@ -310,6 +310,23 @@ class _SettingsPageState extends State<SettingsPage> {
})
],
),
height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text(tr(
'onlyCheckInstalledOrTrackOnlyApps'))),
Switch(
value: settingsProvider
.onlyCheckInstalledOrTrackOnlyApps,
onChanged: (value) {
settingsProvider
.onlyCheckInstalledOrTrackOnlyApps =
value;
})
],
),
height32,
Text(
tr('sourceSpecific'),
@ -484,6 +501,21 @@ class _SettingsPageState extends State<SettingsPage> {
})
],
),
height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text(tr('highlightTouchTargets'))),
Switch(
value:
settingsProvider.highlightTouchTargets,
onChanged: (value) {
settingsProvider.highlightTouchTargets =
value;
})
],
),
height32,
Text(
tr('categories'),
@ -520,7 +552,8 @@ class _SettingsPageState extends State<SettingsPage> {
onPressed: () {
context.read<LogsProvider>().get().then((logs) {
if (logs.isEmpty) {
showError(ObtainiumError(tr('noLogs')), context);
showMessage(
ObtainiumError(tr('noLogs')), context);
} else {
showDialog(
context: context,
@ -543,7 +576,7 @@ class _SettingsPageState extends State<SettingsPage> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Flexible(child: Text('Debug Menu')),
Flexible(child: Text(tr('debugMenu'))),
Switch(
value: settingsProvider.showDebugOpts,
onChanged: (value) {
@ -562,12 +595,9 @@ class _SettingsPageState extends State<SettingsPage> {
const Duration(seconds: 0),
bgUpdateCheckAlarmId + 200,
bgUpdateCheck);
showError(
'Background task started - check logs.',
context);
showMessage(tr('bgTaskStarted'), context);
},
child:
const Text('Run Background Update Check Now'))
child: Text(tr('runBgCheckNow')))
],
),
]),

View File

@ -5,6 +5,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:http/http.dart' as http;
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
import 'package:android_intent_plus/flag.dart';
@ -30,6 +31,7 @@ import 'package:obtainium/providers/source_provider.dart';
import 'package:http/http.dart';
import 'package:android_intent_plus/android_intent.dart';
import 'package:flutter_archive/flutter_archive.dart';
import 'package:shared_storage/shared_storage.dart' as saf;
final pm = AndroidPackageManager();
@ -149,6 +151,7 @@ class AppsProvider with ChangeNotifier {
late Stream<FGBGType>? foregroundStream;
late StreamSubscription<FGBGType>? foregroundSubscription;
late Directory APKDir;
late SettingsProvider settingsProvider = SettingsProvider();
Iterable<AppInMemory> getAppValues() => apps.values.map((a) => a.deepCopy());
@ -160,6 +163,7 @@ class AppsProvider with ChangeNotifier {
if (isForeground) await loadApps();
});
() async {
await settingsProvider.initializeSettings();
var cacheDirs = await getExternalCacheDirectories();
if (cacheDirs?.isNotEmpty ?? false) {
APKDir = cacheDirs!.first;
@ -215,7 +219,7 @@ class AppsProvider with ChangeNotifier {
if (headers != null) {
req.headers.addAll(headers);
}
var client = Client();
var client = http.Client();
StreamedResponse response = await client.send(req);
String ext =
response.headers['content-disposition']?.split('.').last ?? 'apk';
@ -259,25 +263,30 @@ class AppsProvider with ChangeNotifier {
return downloadedFile;
}
Future<File> handleAPKIDChange(App app, PackageInfo newInfo,
Future<File> handleAPKIDChange(App app, PackageInfo? newInfo,
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
// The former case should be handled (give the App its real ID), the latter is a security issue
if (app.id != newInfo.packageName) {
var isTempId = SourceProvider().isTempId(app);
if (apps[app.id] != null && !isTempId && !app.allowIdChange) {
throw IDChangedError(newInfo.packageName!);
}
var idChangeWasAllowed = app.allowIdChange;
app.allowIdChange = false;
var originalAppId = app.id;
app.id = newInfo.packageName!;
downloadedFile = downloadedFile.renameSync(
'${downloadedFile.parent.path}/${app.id}-${downloadUrl.hashCode}.${downloadedFile.path.split('.').last}');
if (apps[originalAppId] != null) {
await removeApps([originalAppId]);
await saveApps([app], onlyIfExists: !isTempId && !idChangeWasAllowed);
var isTempIdBool = isTempId(app);
if (newInfo != null) {
if (app.id != newInfo.packageName) {
if (apps[app.id] != null && !isTempIdBool && !app.allowIdChange) {
throw IDChangedError(newInfo.packageName!);
}
var idChangeWasAllowed = app.allowIdChange;
app.allowIdChange = false;
var originalAppId = app.id;
app.id = newInfo.packageName!;
downloadedFile = downloadedFile.renameSync(
'${downloadedFile.parent.path}/${app.id}-${downloadUrl.hashCode}.${downloadedFile.path.split('.').last}');
if (apps[originalAppId] != null) {
await removeApps([originalAppId]);
await saveApps([app],
onlyIfExists: !isTempIdBool && !idChangeWasAllowed);
}
}
} else if (isTempIdBool) {
throw ObtainiumError('Could not get ID from APK');
}
return downloadedFile;
}
@ -298,9 +307,11 @@ class AppsProvider with ChangeNotifier {
notificationsProvider?.cancel(notif.id);
int? prevProg;
var fileNameNoExt = '${app.id}-${downloadUrl.hashCode}';
var headers = await source.getRequestHeaders(
additionalSettings: app.additionalSettings, forAPKDownload: true);
var downloadedFile = await downloadFileWithRetry(
downloadUrl, fileNameNoExt, headers: source.requestHeaders,
(double? progress) {
downloadUrl, fileNameNoExt,
headers: headers, (double? progress) {
int? prog = progress?.ceil();
if (apps[app.id] != null) {
apps[app.id]!.downloadProgress = progress;
@ -338,7 +349,7 @@ class AppsProvider with ChangeNotifier {
await pm.getPackageArchiveInfo(archiveFilePath: apks.first.path);
}
downloadedFile =
await handleAPKIDChange(app, newInfo!, downloadedFile, downloadUrl);
await handleAPKIDChange(app, newInfo, downloadedFile, downloadUrl);
// Delete older versions of the file if any
for (var file in downloadedFile.parent.listSync()) {
var fn = file.path.split('/').last;
@ -366,8 +377,7 @@ class AppsProvider with ChangeNotifier {
.where((element) => element.downloadProgress != null)
.isNotEmpty;
Future<bool> canInstallSilently(
App app, SettingsProvider settingsProvider) async {
Future<bool> canInstallSilently(App app) async {
if (app.id == obtainiumId) {
return false;
}
@ -425,7 +435,8 @@ class AppsProvider with ChangeNotifier {
zipFile: File(filePath), destinationDir: Directory(destinationPath));
}
Future<void> installXApkDir(DownloadedXApkDir dir) async {
Future<void> installXApkDir(DownloadedXApkDir dir,
{bool needsBGWorkaround = false}) async {
// We don't know which APKs in an XAPK are supported by the user's device
// So we try installing all of them and assume success if at least one installed
// If 0 APKs installed, throw the first install error encountered
@ -438,11 +449,12 @@ class AppsProvider with ChangeNotifier {
if (file.path.toLowerCase().endsWith('.apk')) {
try {
somethingInstalled = somethingInstalled ||
await installApk(DownloadedApk(dir.appId, file));
await installApk(DownloadedApk(dir.appId, file),
needsBGWorkaround: needsBGWorkaround);
} catch (e) {
logs.add(
'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')) {
await moveObbFile(file, dir.appId);
@ -450,7 +462,7 @@ class AppsProvider with ChangeNotifier {
}
if (somethingInstalled) {
dir.file.delete(recursive: true);
} else if (errors.content.isNotEmpty) {
} else if (errors.idsByErrorString.isNotEmpty) {
throw errors;
}
} finally {
@ -458,7 +470,8 @@ class AppsProvider with ChangeNotifier {
}
}
Future<bool> installApk(DownloadedApk file) async {
Future<bool> installApk(DownloadedApk file,
{bool needsBGWorkaround = false}) async {
var newInfo =
await pm.getPackageArchiveInfo(archiveFilePath: file.file.path);
PackageInfo? appInfo = await getInstalledInfo(apps[file.appId]!.app.id);
@ -467,6 +480,17 @@ class AppsProvider with ChangeNotifier {
!(await canDowngradeApps())) {
throw DowngradeError();
}
if (needsBGWorkaround) {
// The below 'await' will never return if we are in a background process
// To work around this, we should assume the install will be successful
// So we update the app's installed version first as we will never get to the later code
// We can't conditionally get rid of the 'await' as this causes install fails (BG process times out) - see #896
// TODO: When fixed, update this function and the calls to it accordingly
apps[file.appId]!.app.installedVersion =
apps[file.appId]!.app.latestVersion;
await saveApps([apps[file.appId]!.app],
attemptToCorrectInstallStatus: false);
}
int? code =
await AndroidPackageInstaller.installApk(apkFilePath: file.file.path);
bool installed = false;
@ -536,7 +560,6 @@ class AppsProvider with ChangeNotifier {
getHost(apkUrl.value) != getHost(app.url) &&
context != null) {
// ignore: use_build_context_synchronously
var settingsProvider = context.read<SettingsProvider>();
if (!(settingsProvider.hideAPKOriginWarning) &&
// ignore: use_build_context_synchronously
await showDialog(
@ -557,8 +580,8 @@ class AppsProvider with ChangeNotifier {
// If no BuildContext is provided, apps that require user interaction are ignored
// If user input is needed and the App is in the background, a notification is sent to get the user's attention
// Returns an array of Ids for Apps that were successfully downloaded, regardless of installation result
Future<List<String>> downloadAndInstallLatestApps(List<String> appIds,
BuildContext? context, SettingsProvider settingsProvider,
Future<List<String>> downloadAndInstallLatestApps(
List<String> appIds, BuildContext? context,
{NotificationsProvider? notificationsProvider}) async {
notificationsProvider =
notificationsProvider ?? context?.read<NotificationsProvider>();
@ -587,8 +610,7 @@ class AppsProvider with ChangeNotifier {
apps[id]!.app.preferredApkIndex = urlInd;
await saveApps([apps[id]!.app]);
}
if (context != null ||
await canInstallSilently(apps[id]!.app, settingsProvider)) {
if (context != null || await canInstallSilently(apps[id]!.app)) {
appsToInstall.add(id);
}
}
@ -625,8 +647,7 @@ class AppsProvider with ChangeNotifier {
downloadedDir = downloadedArtifact as DownloadedXApkDir;
}
var appId = downloadedFile?.appId ?? downloadedDir!.appId;
bool willBeSilent =
await canInstallSilently(apps[appId]!.app, settingsProvider);
bool willBeSilent = await canInstallSilently(apps[appId]!.app);
if (!(await settingsProvider.getInstallPermission(enforce: false))) {
throw ObtainiumError(tr('cancelled'));
}
@ -639,15 +660,13 @@ class AppsProvider with ChangeNotifier {
try {
if (downloadedFile != null) {
if (willBeSilent && context == null) {
// Would await forever - workaround - TODO
installApk(downloadedFile);
installApk(downloadedFile, needsBGWorkaround: true);
} else {
await installApk(downloadedFile);
}
} else {
if (willBeSilent && context == null) {
// Would await forever - workaround - TODO
installXApkDir(downloadedDir!);
installXApkDir(downloadedDir!, needsBGWorkaround: true);
} else {
await installXApkDir(downloadedDir!);
}
@ -663,11 +682,11 @@ class AppsProvider with ChangeNotifier {
}
installedIds.add(id);
} 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;
}
@ -675,8 +694,8 @@ class AppsProvider with ChangeNotifier {
}
Future<Directory> getAppsDir() async {
Directory appsDir = Directory(
'${(await getExternalStorageDirectory())?.path as String}/app_data');
Directory appsDir =
Directory('${(await getExternalStorageDirectory())!.path}/app_data');
if (!appsDir.existsSync()) {
appsDir.createSync();
}
@ -695,14 +714,23 @@ class AppsProvider with ChangeNotifier {
}
bool isVersionDetectionPossible(AppInMemory? app) {
return app?.app.additionalSettings['trackOnly'] != true &&
app?.app.additionalSettings['versionDetection'] !=
if (app?.app == null) {
return false;
}
var naiveStandardVersionDetection =
app!.app.additionalSettings['naiveStandardVersionDetection'] == true ||
SourceProvider()
.getSource(app.app.url, overrideSource: app.app.overrideSource)
.naiveStandardVersionDetection;
return app.app.additionalSettings['trackOnly'] != true &&
app.app.additionalSettings['versionDetection'] !=
'releaseDateAsVersion' &&
app?.installedInfo?.versionName != null &&
app?.app.installedVersion != null &&
reconcileVersionDifferences(
app!.installedInfo!.versionName!, app.app.installedVersion!) !=
null;
app.installedInfo?.versionName != null &&
app.app.installedVersion != null &&
(reconcileVersionDifferences(app.installedInfo!.versionName!,
app.app.installedVersion!) !=
null ||
naiveStandardVersionDetection);
}
// Given an App and it's on-device info...
@ -711,8 +739,14 @@ class AppsProvider with ChangeNotifier {
App app, PackageInfo? installedInfo) {
var modded = false;
var trackOnly = app.additionalSettings['trackOnly'] == true;
var noVersionDetection = app.additionalSettings['versionDetection'] !=
'standardVersionDetection';
var versionDetectionIsStandard =
app.additionalSettings['versionDetection'] ==
'standardVersionDetection';
var naiveStandardVersionDetection =
app.additionalSettings['naiveStandardVersionDetection'] == true ||
SourceProvider()
.getSource(app.url, overrideSource: app.overrideSource)
.naiveStandardVersionDetection;
// FIRST, COMPARE THE APP'S REPORTED AND REAL INSTALLED VERSIONS, WHERE ONE IS NULL
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
@ -727,7 +761,7 @@ class AppsProvider with ChangeNotifier {
// SECOND, RECONCILE DIFFERENCES BETWEEN THE APP'S REPORTED AND REAL INSTALLED VERSIONS, WHERE NEITHER IS NULL
if (installedInfo?.versionName != null &&
installedInfo!.versionName != app.installedVersion &&
!noVersionDetection) {
versionDetectionIsStandard) {
// 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
var correctedInstalledVersion = reconcileVersionDifferences(
@ -735,12 +769,15 @@ class AppsProvider with ChangeNotifier {
if (correctedInstalledVersion?.key == false) {
app.installedVersion = correctedInstalledVersion!.value;
modded = true;
} else if (naiveStandardVersionDetection) {
app.installedVersion = installedInfo.versionName;
modded = true;
}
}
// THIRD, RECONCILE THE APP'S REPORTED INSTALLED AND LATEST VERSIONS
if (app.installedVersion != null &&
app.installedVersion != app.latestVersion &&
!noVersionDetection) {
versionDetectionIsStandard) {
// 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
var correctedInstalledVersion =
@ -752,8 +789,7 @@ class AppsProvider with ChangeNotifier {
}
// FOURTH, DISABLE VERSION DETECTION IF ENABLED AND THE REPORTED/REAL INSTALLED VERSIONS ARE NOT STANDARDIZED
if (installedInfo != null &&
app.additionalSettings['versionDetection'] ==
'standardVersionDetection' &&
versionDetectionIsStandard &&
!isVersionDetectionPossible(
AppInMemory(app, null, installedInfo, null))) {
app.additionalSettings['versionDetection'] = 'noVersionDetection';
@ -876,8 +912,6 @@ class AppsProvider with ChangeNotifier {
.toList();
// After reconciliation, delete externally uninstalled Apps if needed
if (removedAppIds.isNotEmpty) {
var settingsProvider = SettingsProvider();
await settingsProvider.initializeSettings();
if (settingsProvider.removeOnExternalUninstall) {
await removeApps(removedAppIds);
}
@ -916,6 +950,7 @@ class AppsProvider with ChangeNotifier {
}
}
notifyListeners();
exportApps(isAuto: true);
}
Future<void> removeApps(List<String> appIds) async {
@ -937,6 +972,7 @@ class AppsProvider with ChangeNotifier {
}
if (appIds.isNotEmpty) {
notifyListeners();
exportApps(isAuto: true);
}
}
@ -1024,12 +1060,21 @@ class AppsProvider with ChangeNotifier {
}
List<String> getAppsSortedByUpdateCheckTime(
{DateTime? ignoreAppsCheckedAfter}) {
{DateTime? ignoreAppsCheckedAfter,
bool onlyCheckInstalledOrTrackOnlyApps = false}) {
List<String> appIds = apps.values
.where((app) =>
app.app.lastUpdateCheck == null ||
ignoreAppsCheckedAfter == null ||
app.app.lastUpdateCheck!.isBefore(ignoreAppsCheckedAfter))
.where((app) {
if (!onlyCheckInstalledOrTrackOnlyApps) {
return true;
} else {
return app.app.installedVersion != null ||
app.app.additionalSettings['trackOnly'] == true;
}
})
.map((e) => e.app.id)
.toList();
appIds.sort((a, b) =>
@ -1041,35 +1086,46 @@ class AppsProvider with ChangeNotifier {
Future<List<App>> checkUpdates(
{DateTime? ignoreAppsCheckedAfter,
bool throwErrorsForRetry = false}) async {
bool throwErrorsForRetry = false,
List<String>? specificIds,
SettingsProvider? sp}) async {
SettingsProvider settingsProvider = sp ?? this.settingsProvider;
List<App> updates = [];
MultiAppMultiError errors = MultiAppMultiError();
if (!gettingUpdates) {
gettingUpdates = true;
try {
List<String> appIds = getAppsSortedByUpdateCheckTime(
ignoreAppsCheckedAfter: ignoreAppsCheckedAfter);
for (int i = 0; i < appIds.length; i++) {
ignoreAppsCheckedAfter: ignoreAppsCheckedAfter,
onlyCheckInstalledOrTrackOnlyApps:
settingsProvider.onlyCheckInstalledOrTrackOnlyApps);
if (specificIds != null) {
appIds = appIds.where((aId) => specificIds.contains(aId)).toList();
}
await Future.wait(appIds.map((appId) async {
App? newApp;
try {
newApp = await checkUpdate(appIds[i]);
newApp = await checkUpdate(appId);
} catch (e) {
if ((e is RateLimitError || e is SocketException) &&
throwErrorsForRetry) {
rethrow;
}
errors.add(appIds[i], e.toString());
errors.add(appId, e, appName: apps[appId]?.name);
}
if (newApp != null) {
updates.add(newApp);
}
}
}), eagerError: true);
} finally {
gettingUpdates = false;
}
}
if (errors.content.isNotEmpty) {
throw errors;
if (errors.idsByErrorString.isNotEmpty) {
var res = <String, dynamic>{};
res['errors'] = errors;
res['updates'] = updates;
throw res;
}
return updates;
}
@ -1093,32 +1149,49 @@ class AppsProvider with ChangeNotifier {
return updateAppIds;
}
Future<String> exportApps() async {
if ((await DeviceInfoPlugin().androidInfo).version.sdkInt <= 29) {
if (await Permission.storage.isDenied) {
await Permission.storage.request();
Future<String?> exportApps(
{bool pickOnly = false, isAuto = false, SettingsProvider? sp}) async {
SettingsProvider settingsProvider = sp ?? this.settingsProvider;
var exportDir = await settingsProvider.getExportDir();
if (isAuto) {
if (settingsProvider.autoExportOnChanges != true) {
return null;
}
if (await Permission.storage.isDenied) {
throw ObtainiumError(tr('storagePermissionDenied'));
if (exportDir == null) {
return null;
}
var files = await saf
.listFiles(exportDir, columns: [saf.DocumentFileColumn.id])
.where((f) => f.uri.pathSegments.last.endsWith('-auto.json'))
.toList();
if (files.isNotEmpty) {
for (var f in files) {
saf.delete(f.uri);
}
}
}
Directory? exportDir = Directory('/storage/emulated/0/Download');
String path = 'Downloads'; // TODO: See if hardcoding this can be avoided
var downloadsAccessible = false;
try {
downloadsAccessible = exportDir.existsSync();
} catch (e) {
logs.add('Error accessing Downloads (will use fallback): $e');
if (exportDir == null || pickOnly) {
await settingsProvider.pickExportDir();
exportDir = await settingsProvider.getExportDir();
}
if (!downloadsAccessible) {
exportDir = await getExternalStorageDirectory();
path = exportDir!.path;
if (exportDir == null) {
return null;
}
File export = File(
'${exportDir.path}/${tr('obtainiumExportHyphenatedLowercase')}-${DateTime.now().millisecondsSinceEpoch}.json');
export.writeAsStringSync(
jsonEncode(apps.values.map((e) => e.app.toJson()).toList()));
return path;
String? returnPath;
if (!pickOnly) {
var result = await saf.createFile(exportDir,
displayName:
'${tr('obtainiumExportHyphenatedLowercase')}-${DateTime.now().toIso8601String().replaceAll(':', '-')}${isAuto ? '-auto' : ''}.json',
mimeType: 'application/json',
bytes: Uint8List.fromList(utf8.encode(
jsonEncode(apps.values.map((e) => e.app.toJson()).toList()))));
if (result == null) {
throw ObtainiumError(tr('unexpectedError'));
}
returnPath =
exportDir.pathSegments.join('/').replaceFirst('tree/primary:', '/');
}
return returnPath;
}
Future<int> importApps(String appsJSON) async {
@ -1268,22 +1341,21 @@ class _APKOriginWarningDialogState extends State<APKOriginWarningDialog> {
/// 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 int? attemptCount: The number of times the function has failed up to this point (defaults to 0)
/// @param List<String>? toInstall: The appIds to attempt to update (if empty - which is the default - all pending updates are taken)
///
/// 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.
/// If an update is available, the appId is either added to toInstall (if a background update is possible) or the user is notified.
/// If there is an error, the function tries to continue after a few minutes (duration depends on the error), up to a maximum of 5 tries.
/// In update mode, all apps in toCheck are checked for updates (in parallel).
/// If an update is available and it cannot be installed silently, the user is notified of the available update.
/// 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.
/// In this mode, all apps in toInstall are downloaded and installed in the background (install result is unknown).
/// If there is an error, the function tries to continue after a few minutes (duration depends on the error), up to a maximum of 5 tries.
/// Once all update checks are complete, the task is run again in install mode.
/// 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 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')
Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
WidgetsFlutterBinding.ensureInitialized();
@ -1295,14 +1367,12 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
NotificationsProvider notificationsProvider = NotificationsProvider();
AppsProvider appsProvider = AppsProvider(isBg: true);
await appsProvider.loadApps();
var settingsProvider = SettingsProvider();
await settingsProvider.initializeSettings();
int maxAttempts = 4;
params ??= {};
if (params['toCheck'] == null) {
settingsProvider.lastBGCheckTime = DateTime.now();
appsProvider.settingsProvider.lastBGCheckTime = DateTime.now();
}
List<MapEntry<String, int>> toCheck = <MapEntry<String, int>>[
...(params['toCheck']
@ -1310,7 +1380,9 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
entry['key'] as String, entry['value'] as int))
.toList() ??
appsProvider
.getAppsSortedByUpdateCheckTime()
.getAppsSortedByUpdateCheckTime(
onlyCheckInstalledOrTrackOnlyApps: appsProvider
.settingsProvider.onlyCheckInstalledOrTrackOnlyApps)
.map((e) => MapEntry(e, 0)))
];
List<MapEntry<String, int>> toInstall = <MapEntry<String, int>>[
@ -1321,95 +1393,154 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
(<List<MapEntry<String, int>>>[]))
];
bool installMode = toCheck.isEmpty &&
toInstall.isNotEmpty; // Task is either in update mode or install mode
var netResult = await (Connectivity().checkConnectivity());
if (netResult == ConnectivityResult.none) {
var networkBasedRetryInterval = 15;
var nextRegularCheck = appsProvider.settingsProvider.lastBGCheckTime
.add(Duration(minutes: appsProvider.settingsProvider.updateInterval));
var potentialNetworkRetryCheck =
DateTime.now().add(Duration(minutes: networkBasedRetryInterval));
var shouldRetry = potentialNetworkRetryCheck.isBefore(nextRegularCheck);
logs.add(
'BG update task $taskId: No network. Will ${shouldRetry ? 'retry in $networkBasedRetryInterval minutes' : 'not retry'}.');
AndroidAlarmManager.oneShot(
const Duration(minutes: 15), taskId + 1, bgUpdateCheck,
params: {
'toCheck': toCheck
.map((entry) => {'key': entry.key, 'value': entry.value})
.toList(),
'toInstall': toInstall
.map((entry) => {'key': entry.key, 'value': entry.value})
.toList(),
});
return;
}
var networkRestricted = false;
if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) {
networkRestricted = (netResult != ConnectivityResult.wifi) &&
(netResult != ConnectivityResult.ethernet);
}
bool installMode =
toCheck.isEmpty; // Task is either in update mode or install mode
logs.add(
'BG ${installMode ? 'install' : 'update'} task $taskId: Started (${installMode ? toInstall.length : toCheck.length}).');
if (!installMode) {
// If in update mode...
var didCompleteChecking = false;
CheckingUpdatesNotification? notif;
// If in update mode, we check for updates.
// We divide the results into 4 groups:
// - toNotify - Apps with updates that the user will be notified about (can't be silently installed)
// - toRetry - Apps with update check errors that will be retried in a while
// - toThrow - Apps with update check errors that the user will be notified about (no retry)
// 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;
if (settingsProvider.bgUpdatesOnWiFiOnly) {
if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) {
var netResult = await (Connectivity().checkConnectivity());
networkRestricted = (netResult != ConnectivityResult.wifi) &&
(netResult != ConnectivityResult.ethernet);
}
// Loop through all updates and check each
List<App> toNotify = [];
try {
for (int i = 0; i < toCheck.length; i++) {
var appId = toCheck[i].key;
var attemptCount = toCheck[i].value + 1;
AppInMemory? app = appsProvider.apps[appId];
if (app?.app.installedVersion != null) {
try {
notificationsProvider.notify(
notif = CheckingUpdatesNotification(app?.name ?? appId),
cancelExisting: true);
App? newApp = await appsProvider.checkUpdate(appId);
if (newApp != null) {
if (networkRestricted ||
!(await appsProvider.canInstallSilently(
app!.app, settingsProvider))) {
toNotify.add(newApp);
} else {
toInstall.add(MapEntry(appId, 0));
}
}
if (i == (toCheck.length - 1)) {
didCompleteChecking = true;
}
} catch (e) {
// If you got an error, move the offender to the back of the line (increment their fail count) and schedule another task to continue checking shortly
logs.add(
'BG update task $taskId: Got error on checking for $appId \'${e.toString()}\'.');
if (attemptCount < maxAttempts) {
var remainingSeconds = e is RateLimitError
? (i == 0 ? (e.remainingMinutes * 60) : (5 * 60))
: e is ClientException
? (15 * 60)
: pow(attemptCount, 2).toInt();
logs.add(
'BG update task $taskId: Will continue in $remainingSeconds seconds (with $appId moved to the end of the line).');
var remainingToCheck = moveStrToEndMapEntryWithCount(
toCheck.sublist(i), MapEntry(appId, attemptCount));
AndroidAlarmManager.oneShot(Duration(seconds: remainingSeconds),
taskId + 1, bgUpdateCheck,
params: {
'toCheck': remainingToCheck
.map(
(entry) => {'key': entry.key, 'value': entry.value})
.toList(),
'toInstall': toInstall
.map(
(entry) => {'key': entry.key, 'value': entry.value})
.toList(),
});
break;
} else {
// 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);
// Check for updates
notificationsProvider.notify(notif, cancelExisting: true);
updates = await appsProvider.checkUpdates(
specificIds: toCheck.map((e) => e.key).toList(),
sp: appsProvider.settingsProvider);
} catch (e) {
// If there were errors, group them into toRetry and toThrow based on max retry count per app
if (e is Map) {
updates = e['updates'];
errors = e['errors'];
errors!.rawErrors.forEach((key, err) {
logs.add(
'BG update task $taskId: Got error on checking for $key \'${err.toString()}\'.');
var toCheckApp = toCheck.where((element) => element.key == key).first;
if (toCheckApp.value < maxAttempts) {
toRetry.add(MapEntry(toCheckApp.key, toCheckApp.value + 1));
// Next task interval is based on the error with the longest retry time
var minRetryIntervalForThisApp = err is RateLimitError
? (err.remainingMinutes * 60)
: e is ClientException
? (15 * 60)
: pow(toCheckApp.value + 1, 2).toInt();
if (minRetryIntervalForThisApp > retryAfterXSeconds) {
retryAfterXSeconds = minRetryIntervalForThisApp;
}
} 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 {
if (toNotify.isNotEmpty) {
notificationsProvider.notify(UpdateNotification(toNotify));
notificationsProvider.cancel(notif.id);
}
// 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(
'BG update task $taskId: Done. Scheduling install task to run immediately.');
AndroidAlarmManager.oneShot(
@ -1420,11 +1551,19 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
.map((entry) => {'key': entry.key, 'value': entry.value})
.toList()
});
} else if (didCompleteChecking) {
logs.add('BG install task $taskId: Done.');
}
} 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 tempObtArr = toInstall.where((element) => element.key == obtainiumId);
if (tempObtArr.isNotEmpty) {
@ -1439,8 +1578,7 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
try {
logs.add(
'BG install task $taskId: Attempting to update $appId in the background.');
await appsProvider.downloadAndInstallLatestApps(
[appId], null, settingsProvider,
await appsProvider.downloadAndInstallLatestApps([appId], null,
notificationsProvider: notificationsProvider);
await Future.delayed(const Duration(
seconds:
@ -1477,9 +1615,9 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
.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'),
'',
'UPDATES_AVAILABLE',
tr('updatesAvailable'),
tr('updatesAvailableNotifChannel'),
tr('updatesAvailableNotifDescription'),
Importance.max) {
message = updates.isEmpty
@ -42,8 +42,14 @@ class UpdateNotification extends ObtainiumNotification {
class SilentUpdateNotification extends ObtainiumNotification {
SilentUpdateNotification(List<App> updates, {int? id})
: super(id ?? 3, tr('appsUpdated'), '', 'APPS_UPDATED', tr('appsUpdated'),
tr('appsUpdatedNotifDescription'), Importance.defaultImportance) {
: super(
id ?? 3,
tr('appsUpdated'),
'',
'APPS_UPDATED',
tr('appsUpdatedNotifChannel'),
tr('appsUpdatedNotifDescription'),
Importance.defaultImportance) {
message = updates.length == 1
? tr('xWasUpdatedToY',
args: [updates[0].finalName, updates[0].latestVersion])
@ -59,7 +65,7 @@ class SilentUpdateAttemptNotification extends ObtainiumNotification {
tr('appsPossiblyUpdated'),
'',
'APPS_POSSIBLY_UPDATED',
tr('appsPossiblyUpdated'),
tr('appsPossiblyUpdatedNotifChannel'),
tr('appsPossiblyUpdatedNotifDescription'),
Importance.defaultImportance) {
message = updates.length == 1
@ -77,15 +83,21 @@ class ErrorCheckingUpdatesNotification extends ObtainiumNotification {
tr('errorCheckingUpdates'),
error,
'BG_UPDATE_CHECK_ERROR',
tr('errorCheckingUpdates'),
tr('errorCheckingUpdatesNotifChannel'),
tr('errorCheckingUpdatesNotifDescription'),
Importance.high);
}
class AppsRemovedNotification extends ObtainiumNotification {
AppsRemovedNotification(List<List<String>> namedReasons)
: super(6, tr('appsRemoved'), '', 'APPS_REMOVED', tr('appsRemoved'),
tr('appsRemovedNotifDescription'), Importance.max) {
: super(
6,
tr('appsRemoved'),
'',
'APPS_REMOVED',
tr('appsRemovedNotifChannel'),
tr('appsRemovedNotifDescription'),
Importance.max) {
message = '';
for (var r in namedReasons) {
message += '${tr('xWasRemovedDueToErrorY', args: [r[0], r[1]])} \n';
@ -101,7 +113,7 @@ class DownloadNotification extends ObtainiumNotification {
tr('downloadingX', args: [appName]),
'',
'APP_DOWNLOADING',
tr('downloadingX', args: [tr('app')]),
tr('downloadingXNotifChannel', args: [tr('app')]),
tr('downloadNotifDescription'),
Importance.low,
onlyAlertOnce: true,
@ -113,7 +125,7 @@ final completeInstallationNotification = ObtainiumNotification(
tr('completeAppInstallation'),
tr('obtainiumMustBeOpenToInstallApps'),
'COMPLETE_INSTALL',
tr('completeAppInstallation'),
tr('completeAppInstallationNotifChannel'),
tr('completeAppInstallationNotifDescription'),
Importance.max);
@ -124,7 +136,7 @@ class CheckingUpdatesNotification extends ObtainiumNotification {
tr('checkingForUpdates'),
appName,
'BG_UPDATE_CHECK',
tr('checkingForUpdates'),
tr('checkingForUpdatesNotifChannel'),
tr('checkingForUpdatesNotifDescription'),
Importance.min);
}

View File

@ -9,8 +9,10 @@ import 'package:obtainium/app_sources/github.dart';
import 'package:obtainium/main.dart';
import 'package:obtainium/providers/apps_provider.dart';
import 'package:obtainium/providers/source_provider.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:shared_storage/shared_storage.dart' as saf;
String obtainiumTempId = 'imranr98_obtainium_${GitHub().host}';
String obtainiumId = 'dev.imranr.obtainium';
@ -35,6 +37,7 @@ List<int> updateIntervals = [15, 30, 60, 120, 180, 360, 720, 1440, 4320, 0]
class SettingsProvider with ChangeNotifier {
SharedPreferences? prefs;
String? defaultAppDir;
bool justStarted = true;
String sourceUrl = 'https://github.com/ImranR98/Obtainium';
@ -42,6 +45,7 @@ class SettingsProvider with ChangeNotifier {
// Not done in constructor as we want to be able to await it
Future<void> initializeSettings() async {
prefs = await SharedPreferences.getInstance();
defaultAppDir = (await getExternalStorageDirectory())!.path;
notifyListeners();
}
@ -348,4 +352,67 @@ class SettingsProvider with ChangeNotifier {
prefs?.setBool('showDebugOpts', val);
notifyListeners();
}
bool get highlightTouchTargets {
return prefs?.getBool('highlightTouchTargets') ?? false;
}
set highlightTouchTargets(bool val) {
prefs?.setBool('highlightTouchTargets', val);
notifyListeners();
}
Future<Uri?> getExportDir() async {
var uriString = prefs?.getString('exportDir');
if (uriString != null) {
Uri? uri = Uri.parse(uriString);
if (!(await saf.canRead(uri) ?? false) ||
!(await saf.canWrite(uri) ?? false)) {
uri = null;
prefs?.remove('exportDir');
notifyListeners();
}
return uri;
} else {
return null;
}
}
Future<void> pickExportDir({bool remove = false}) async {
var existingSAFPerms = (await saf.persistedUriPermissions()) ?? [];
var currentOneWayDataSyncDir = await getExportDir();
Uri? newOneWayDataSyncDir;
if (!remove) {
newOneWayDataSyncDir = (await saf.openDocumentTree());
}
if (currentOneWayDataSyncDir?.path != newOneWayDataSyncDir?.path) {
if (newOneWayDataSyncDir == null) {
prefs?.remove('exportDir');
} else {
prefs?.setString('exportDir', newOneWayDataSyncDir.toString());
}
notifyListeners();
}
for (var e in existingSAFPerms) {
await saf.releasePersistableUriPermission(e.uri);
}
}
bool get autoExportOnChanges {
return prefs?.getBool('autoExportOnChanges') ?? false;
}
set autoExportOnChanges(bool val) {
prefs?.setBool('autoExportOnChanges', val);
notifyListeners();
}
bool get onlyCheckInstalledOrTrackOnlyApps {
return prefs?.getBool('onlyCheckInstalledOrTrackOnlyApps') ?? false;
}
set onlyCheckInstalledOrTrackOnlyApps(bool val) {
prefs?.setBool('onlyCheckInstalledOrTrackOnlyApps', val);
notifyListeners();
}
}

View File

@ -26,7 +26,9 @@ import 'package:obtainium/app_sources/sourceforge.dart';
import 'package:obtainium/app_sources/sourcehut.dart';
import 'package:obtainium/app_sources/steammobile.dart';
import 'package:obtainium/app_sources/telegramapp.dart';
import 'package:obtainium/app_sources/uptodown.dart';
import 'package:obtainium/app_sources/vlc.dart';
import 'package:obtainium/app_sources/whatsapp.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/mass_app_sources/githubstars.dart';
@ -281,9 +283,6 @@ preStandardizeUrl(String url) {
url.toLowerCase().indexOf('https://') != 0) {
url = 'https://$url';
}
if (url.toLowerCase().indexOf('https://www.') == 0) {
url = 'https://${url.substring(12)}';
}
url = url
.split('/')
.where((e) => e.isNotEmpty)
@ -327,6 +326,8 @@ abstract class AppSource {
bool changeLogIfAnyIsMarkDown = true;
bool appIdInferIsOptional = false;
bool allowSubDomains = false;
bool naiveStandardVersionDetection = false;
bool neverAutoSelect = false;
AppSource() {
name = runtimeType.toString();
@ -363,15 +364,27 @@ abstract class AppSource {
return url;
}
Map<String, String>? get requestHeaders => null;
Future<Map<String, String>?> getRequestHeaders(
{Map<String, dynamic> additionalSettings = const <String, dynamic>{},
bool forAPKDownload = false}) async {
return null;
}
App endOfGetAppChanges(App app) {
return app;
}
Future<Response> sourceRequest(String url,
{bool followRedirects = true}) async {
{bool followRedirects = true,
Map<String, dynamic> additionalSettings =
const <String, dynamic>{}}) async {
var requestHeaders =
await getRequestHeaders(additionalSettings: additionalSettings);
if (requestHeaders != null || followRedirects == false) {
var req = Request('GET', Uri.parse(url));
req.followRedirects = followRedirects;
if (requestHeaders != null) {
req.headers.addAll(requestHeaders!);
req.headers.addAll(requestHeaders);
}
return Response.fromStream(await Client().send(req));
} else {
@ -431,6 +444,10 @@ abstract class AppSource {
[
GeneratedFormSwitch('exemptFromBackgroundUpdates',
label: tr('exemptFromBackgroundUpdates'))
],
[
GeneratedFormSwitch('skipUpdateNotifications',
label: tr('skipUpdateNotifications'))
]
];
@ -474,6 +491,7 @@ abstract class AppSource {
}
bool canSearch = false;
bool excludeFromMassSearch = false;
List<GeneratedFormItem> searchQuerySettingFormItems = [];
Future<Map<String, List<String>>> search(String query,
{Map<String, dynamic> querySettings = const {}}) {
@ -512,6 +530,25 @@ regExValidator(String? value) {
return null;
}
intValidator(String? value, {bool positive = false}) {
if (value == null) {
return tr('invalidInput');
}
var num = int.tryParse(value);
if (num == null) {
return tr('invalidInput');
}
if (positive && num <= 0) {
return tr('invalidInput');
}
return null;
}
bool isTempId(App app) {
// return app.id == generateTempID(app.url, app.additionalSettings);
return RegExp('^[0-9]+\$').hasMatch(app.id);
}
class SourceProvider {
// Add more source classes here so they are available via the service
List<AppSource> get sources => [
@ -519,20 +556,21 @@ class SourceProvider {
GitLab(),
Codeberg(),
FDroid(),
IzzyOnDroid(),
FDroidRepo(),
Jenkins(),
IzzyOnDroid(),
SourceForge(),
SourceHut(),
Aptoide(),
APKMirror(),
APKPure(),
Aptoide(),
Uptodown(),
APKMirror(),
HuaweiAppGallery(),
Jenkins(),
// APKCombo(), // Can't get past their scraping blocking yet (get 403 Forbidden)
Mullvad(),
Signal(),
VLC(),
// WhatsApp(), // As of 2023-03-20 this is unusable as the version on the webpage is months out of date
WhatsApp(), // As of 2023-03-20 this is unusable as the version on the webpage is months out of date
TelegramApp(),
SteamMobile(),
NeutronCode(),
@ -558,14 +596,15 @@ class SourceProvider {
AppSource? source;
for (var s in sources.where((element) => element.host != null)) {
if (RegExp(
'://${s.allowSubDomains ? '([^\\.]+\\.)*' : ''}${s.host}(/|\\z)?')
'://(${s.allowSubDomains ? '([^\\.]+\\.)*' : ''}|www\\.)${s.host}(/|\\z)?')
.hasMatch(url)) {
source = s;
break;
}
}
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 {
s.sourceSpecificStandardizeURL(url);
source = s;
@ -596,11 +635,6 @@ class SourceProvider {
String standardUrl, Map<String, dynamic> additionalSettings) =>
(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(
AppSource source, String url, Map<String, dynamic> additionalSettings,
{App? currentApp,
@ -639,10 +673,9 @@ class SourceProvider {
}
}
}
String apkVersion = apk.version.replaceAll('/', '-');
var name = currentApp != null ? currentApp.name.trim() : '';
name = name.isNotEmpty ? name : apk.names.name;
return App(
App finalApp = App(
currentApp?.id ??
((!source.appIdInferIsOptional ||
(source.appIdInferIsOptional && inferAppIdIfOptional))
@ -654,7 +687,7 @@ class SourceProvider {
apk.names.author,
name,
currentApp?.installedVersion,
apkVersion,
apk.version,
apk.apkUrls,
apk.apkUrls.length - 1 >= 0 ? apk.apkUrls.length - 1 : 0,
additionalSettings,
@ -668,6 +701,7 @@ class SourceProvider {
source.appIdInferIsOptional &&
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

View File

@ -5,18 +5,18 @@ packages:
dependency: "direct main"
description:
name: android_alarm_manager_plus
sha256: c20d91a9096596f66274bf8172321c278f9cba8091638f80205fe66d31587fa5
sha256: "82fb28c867c4b3dd7e9157728e46426b8916362f977dbba46b949210f00099f4"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.0.3"
android_intent_plus:
dependency: "direct main"
description:
name: android_intent_plus
sha256: f72ae20bb37108694f442e7ae6acbd28b453ca62ce86842f6787b784355abfe6
sha256: e1c62bb41c90e15083b7fb84dc327fe90396cc9c1445b55ff1082144fabfb4d9
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "4.0.3"
android_package_installer:
dependency: "direct main"
description:
@ -46,10 +46,10 @@ packages:
dependency: transitive
description:
name: archive
sha256: "49b1fad315e57ab0bbc15bcbb874e83116a1d78f77ebd500a4af6c9407d6b28e"
sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03"
url: "https://pub.dev"
source: hosted
version: "3.3.8"
version: "3.4.6"
args:
dependency: transitive
description:
@ -118,10 +118,10 @@ packages:
dependency: "direct main"
description:
name: connectivity_plus
sha256: "77a180d6938f78ca7d2382d2240eb626c0f6a735d0bfdce227d8ffb80f95c48b"
sha256: b502a681ba415272ecc41400bd04fe543ed1a62632137dc84d25a91e7746f55f
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "5.0.1"
connectivity_plus_platform_interface:
dependency: transitive
description:
@ -142,10 +142,10 @@ packages:
dependency: transitive
description:
name: cross_file
sha256: fd832b5384d0d6da4f6df60b854d33accaaeb63aa9e10e736a87381f08dee2cb
sha256: "445db18de832dba8d851e287aff8ccf169bed30d2e94243cb54c7d2f1ed2142c"
url: "https://pub.dev"
source: hosted
version: "0.3.3+5"
version: "0.3.3+6"
crypto:
dependency: transitive
description:
@ -182,10 +182,10 @@ packages:
dependency: "direct main"
description:
name: device_info_plus
sha256: "86add5ef97215562d2e090535b0a16f197902b10c369c558a100e74ea06e8659"
sha256: "7035152271ff67b072a211152846e9f1259cf1be41e34cd3e0b5463d2d6b8419"
url: "https://pub.dev"
source: hosted
version: "9.0.3"
version: "9.1.0"
device_info_plus_platform_interface:
dependency: transitive
description:
@ -198,10 +198,10 @@ packages:
dependency: "direct main"
description:
name: dynamic_color
sha256: de4798a7069121aee12d5895315680258415de9b00e717723a1bd73d58f0126d
sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f"
url: "https://pub.dev"
source: hosted
version: "1.6.6"
version: "1.6.8"
easy_localization:
dependency: "direct main"
description:
@ -238,18 +238,18 @@ packages:
dependency: transitive
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
version: "7.0.0"
file_picker:
dependency: "direct main"
description:
name: file_picker
sha256: be325344c1f3070354a1d84a231a1ba75ea85d413774ec4bdf444c023342e030
sha256: "903dd4ba13eae7cef64acc480e91bf54c3ddd23b5b90b639c170f3911e489620"
url: "https://pub.dev"
source: hosted
version: "5.5.0"
version: "6.0.0"
flutter:
dependency: "direct main"
description: flutter
@ -283,18 +283,18 @@ packages:
dependency: "direct dev"
description:
name: flutter_lints
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
url: "https://pub.dev"
source: hosted
version: "2.0.3"
version: "3.0.1"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
sha256: "3002092e5b8ce2f86c3361422e52e6db6776c23ee21e0b2f71b892bf4259ef04"
sha256: "6d11ea777496061e583623aaf31923f93a9409ef8fcaeeefdd6cd78bf4fe5bb3"
url: "https://pub.dev"
source: hosted
version: "15.1.1"
version: "16.1.0"
flutter_local_notifications_linux:
dependency: transitive
description:
@ -320,18 +320,18 @@ packages:
dependency: "direct main"
description:
name: flutter_markdown
sha256: d4a1cb250c4e059586af0235f32e02882860a508e189b61f2b31b8810c1e1330
sha256: "8afc9a6aa6d8e8063523192ba837149dbf3d377a37c0b0fc579149a1fbd4a619"
url: "https://pub.dev"
source: hosted
version: "0.6.17+2"
version: "0.6.18"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
url: "https://pub.dev"
source: hosted
version: "2.0.16"
version: "2.0.17"
flutter_test:
dependency: "direct dev"
description: flutter
@ -386,10 +386,10 @@ packages:
dependency: transitive
description:
name: image
sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf
sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271"
url: "https://pub.dev"
source: hosted
version: "4.0.17"
version: "4.1.3"
intl:
dependency: transitive
description:
@ -418,10 +418,10 @@ packages:
dependency: transitive
description:
name: lints
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "3.0.0"
markdown:
dependency: transitive
description:
@ -498,10 +498,10 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1"
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.2.1"
path_provider_foundation:
dependency: transitive
description:
@ -538,18 +538,18 @@ packages:
dependency: "direct main"
description:
name: permission_handler
sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81"
sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8"
url: "https://pub.dev"
source: hosted
version: "10.4.3"
version: "11.0.1"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: d74e77a5ecd38649905db0a7d05ef16bed42ff263b9efb73ed794317c5764ec3
sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e
url: "https://pub.dev"
source: hosted
version: "10.3.4"
version: "11.1.0"
permission_handler_apple:
dependency: transitive
description:
@ -562,10 +562,10 @@ packages:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9"
sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4"
url: "https://pub.dev"
source: hosted
version: "3.11.3"
version: "3.12.0"
permission_handler_windows:
dependency: transitive
description:
@ -586,10 +586,10 @@ packages:
dependency: transitive
description:
name: platform
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
version: "3.1.3"
plugin_platform_interface:
dependency: transitive
description:
@ -618,26 +618,26 @@ packages:
dependency: "direct main"
description:
name: share_plus
sha256: "6cec740fa0943a826951223e76218df002804adb588235a8910dc3d6b0654e11"
sha256: f74fc3f1cbd99f39760182e176802f693fa0ec9625c045561cfad54681ea93dd
url: "https://pub.dev"
source: hosted
version: "7.1.0"
version: "7.2.1"
share_plus_platform_interface:
dependency: transitive
description:
name: share_plus_platform_interface
sha256: "357412af4178d8e11d14f41723f80f12caea54cf0d5cd29af9dcdab85d58aea7"
sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956
url: "https://pub.dev"
source: hosted
version: "3.3.0"
version: "3.3.1"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: b7f41bad7e521d205998772545de63ff4e6c97714775902c199353f8bf1511ac
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.2.2"
shared_preferences_android:
dependency: transitive
description:
@ -658,10 +658,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_linux
sha256: c2eb5bf57a2fe9ad6988121609e47d3e07bb3bdca5b6f8444e4cf302428a128a
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
version: "2.3.2"
shared_preferences_platform_interface:
dependency: transitive
description:
@ -682,10 +682,18 @@ packages:
dependency: transitive
description:
name: shared_preferences_windows
sha256: f763a101313bd3be87edffe0560037500967de9c394a714cd598d945517f694f
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
version: "2.3.2"
shared_storage:
dependency: "direct main"
description:
name: shared_storage
sha256: "7c65a9d64f0f5521256be974cfd74010af12196657cec9f9fb7b03b2f11bcaf6"
url: "https://pub.dev"
source: hosted
version: "0.8.0"
sky_engine:
dependency: transitive
description: flutter
@ -699,6 +707,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.0"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
sqflite:
dependency: "direct main"
description:
@ -783,74 +799,74 @@ packages:
dependency: "direct main"
description:
name: url_launcher
sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27"
sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba
url: "https://pub.dev"
source: hosted
version: "6.1.14"
version: "6.2.1"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330
sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def"
url: "https://pub.dev"
source: hosted
version: "6.1.0"
version: "6.2.0"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f"
sha256: "4ac97281cf60e2e8c5cc703b2b28528f9b50c8f7cebc71df6bdf0845f647268a"
url: "https://pub.dev"
source: hosted
version: "6.1.5"
version: "6.2.0"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e
sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
version: "3.1.0"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
url: "https://pub.dev"
source: hosted
version: "3.0.7"
version: "3.1.0"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618"
sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "2.2.0"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5"
sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2"
url: "https://pub.dev"
source: hosted
version: "2.0.20"
version: "2.2.0"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069"
sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc"
url: "https://pub.dev"
source: hosted
version: "3.0.8"
version: "3.1.0"
uuid:
dependency: transitive
description:
name: uuid
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
sha256: b715b8d3858b6fa9f68f87d20d98830283628014750c2b09b6f516c1da4af2a7
url: "https://pub.dev"
source: hosted
version: "3.0.7"
version: "4.1.0"
vector_math:
dependency: transitive
description:
@ -871,50 +887,50 @@ packages:
dependency: "direct main"
description:
name: webview_flutter
sha256: "82f6787d5df55907aa01e49bd9644f4ed1cc82af7a8257dd9947815959d2e755"
sha256: "42393b4492e629aa3a88618530a4a00de8bb46e50e7b3993fedbfdc5352f0dbf"
url: "https://pub.dev"
source: hosted
version: "4.2.4"
version: "4.4.2"
webview_flutter_android:
dependency: transitive
description:
name: webview_flutter_android
sha256: "0d8f5ac96a155e672129bf94c7abf625de01241d44d269dbaff083f1b4deb1aa"
sha256: "8326ee235f87605a2bfc444a4abc897f4abc78d83f054ba7d3d1074ce82b4fbf"
url: "https://pub.dev"
source: hosted
version: "3.9.5"
version: "3.12.1"
webview_flutter_platform_interface:
dependency: transitive
description:
name: webview_flutter_platform_interface
sha256: "9d32a63a5ee111b37482cb3eac3379b9f0992afd27a52ee30279dbf06f41918b"
sha256: "6d9213c65f1060116757a7c473247c60f3f7f332cac33dc417c9e362a9a13e4f"
url: "https://pub.dev"
source: hosted
version: "2.5.1"
version: "2.6.0"
webview_flutter_wkwebview:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: d2f7241849582da80b79acb03bb936422412ce5c0c79fb5f6a1de5421a5aecc4
sha256: af6f5ab05918070b33507b0d453ba9fb7d39338a3256c23cf9433dc68100774a
url: "https://pub.dev"
source: hosted
version: "3.7.4"
version: "3.9.3"
win32:
dependency: transitive
description:
name: win32
sha256: "9e82a402b7f3d518fb9c02d0e9ae45952df31b9bf34d77baf19da2de03fc2aaa"
sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
url: "https://pub.dev"
source: hosted
version: "5.0.7"
version: "5.0.9"
win32_registry:
dependency: transitive
description:
name: win32_registry
sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9
sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.1.2"
xdg_directories:
dependency: transitive
description:

View File

@ -17,10 +17,10 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 0.14.7+199 # When changing this, update the tag in main() accordingly
version: 0.14.32+226 # When changing this, update the tag in main() accordingly
environment:
sdk: '>=2.18.2 <3.0.0'
sdk: '>=3.0.0 <4.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
@ -38,7 +38,7 @@ dependencies:
cupertino_icons: ^1.0.5
path_provider: ^2.0.11
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
http: ^1.0.0
webview_flutter: ^4.0.0
@ -46,10 +46,10 @@ dependencies:
html: ^0.15.0
shared_preferences: ^2.0.15
url_launcher: ^6.1.5
permission_handler: ^10.0.0
permission_handler: ^11.0.0
fluttertoast: ^8.0.9
device_info_plus: ^9.0.0
file_picker: ^5.2.10
file_picker: ^6.0.0
animations: ^2.0.4
android_package_installer:
git:
@ -64,7 +64,8 @@ dependencies:
flutter_markdown: ^0.6.14
flutter_archive: ^5.0.0
hsluv: ^1.1.3
connectivity_plus: ^4.0.2
connectivity_plus: ^5.0.0
shared_storage: ^0.8.0
dev_dependencies:
flutter_test:
@ -76,7 +77,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.1
flutter_lints: ^3.0.0
flutter_launcher_icons:
android: "ic_launcher"