Compare commits
526 Commits
v0.14.19-b
...
v1.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
badf32ff11 | ||
|
|
5e40f3264e | ||
|
|
71bb6d9410 | ||
|
|
731b682fc9 | ||
|
|
c9751227a5 | ||
|
|
6ef2a26e94 | ||
|
|
5a8efa2388 | ||
|
|
d25895fa28 | ||
|
|
de09f3ece2 | ||
|
|
1135ffb30f | ||
|
|
5379cb31e8 | ||
|
|
7d41ab44b7 | ||
|
|
b239f9bd05 | ||
|
|
1acc923ec2 | ||
|
|
8d6edfb91f | ||
|
|
491b62bb0d | ||
|
|
d1413fc478 | ||
|
|
761f7039e9 | ||
|
|
59783a341d | ||
|
|
0100fa4236 | ||
|
|
f6f9ed68dc | ||
|
|
8adb5e84d1 | ||
|
|
3940b65156 | ||
|
|
98e7f27eab | ||
|
|
7cf5cafd75 | ||
|
|
d750e35273 | ||
|
|
1f12e12130 | ||
|
|
4a4fb4716e | ||
|
|
efe6ec0d43 | ||
|
|
00d605e195 | ||
|
|
f06de8d19f | ||
|
|
fac335c849 | ||
|
|
5bef546ae4 | ||
|
|
b690cd6a67 | ||
|
|
dbee4a2657 | ||
|
|
67420f293b | ||
|
|
d1d6b0792b | ||
|
|
2ec039ba0f | ||
|
|
4b9b9a10ad | ||
|
|
4193d8ada1 | ||
|
|
4701b090b0 | ||
|
|
8b0c85827c | ||
|
|
57e02d1c80 | ||
|
|
4446929b10 | ||
|
|
c31f65b0e4 | ||
|
|
f6c3b8dceb | ||
|
|
10bd825972 | ||
|
|
303bee7c57 | ||
|
|
369ae43062 | ||
|
|
62dc7fd88b | ||
|
|
4aace63cd7 | ||
|
|
5d588a05a7 | ||
|
|
cab6f9d026 | ||
|
|
fc94d553eb | ||
|
|
2952345585 | ||
|
|
becc3828b7 | ||
|
|
2a62248991 | ||
|
|
eb2b31219c | ||
|
|
55053f9627 | ||
|
|
5ff6c64938 | ||
|
|
c1c517cf38 | ||
|
|
b3de2e5d6b | ||
|
|
0183070b2b | ||
|
|
089bbc3ee9 | ||
|
|
b085801c2c | ||
|
|
ad4cae4288 | ||
|
|
95c285e1f7 | ||
|
|
c31a1912a5 | ||
|
|
99da1f8481 | ||
|
|
bf6b3a7da0 | ||
|
|
4ab4f58f65 | ||
|
|
29a76ac8e6 | ||
|
|
e2761ce284 | ||
|
|
dc45334cb9 | ||
|
|
56052bfd79 | ||
|
|
f5067d636a | ||
|
|
b173b1300a | ||
|
|
15a34e53bf | ||
|
|
183fb26988 | ||
|
|
2da9a8cd59 | ||
|
|
bb07000280 | ||
|
|
3a46ad2957 | ||
|
|
6b631d119c | ||
|
|
051762fcc1 | ||
|
|
595d5dc283 | ||
|
|
1d328f07e2 | ||
|
|
b1c8ac6f2a | ||
|
|
da619d37f7 | ||
|
|
236c4722e5 | ||
|
|
92f59116a0 | ||
|
|
c28040f0cb | ||
|
|
e8b9654320 | ||
|
|
be1a793a37 | ||
|
|
440720afb6 | ||
|
|
900d3e734e | ||
|
|
0bf096abb5 | ||
|
|
632efb9e22 | ||
|
|
bfc506e450 | ||
|
|
ffe612708c | ||
|
|
5d161160aa | ||
|
|
eadf3e5a29 | ||
|
|
6b6b4084a0 | ||
|
|
30a4633f72 | ||
|
|
d44139dd72 | ||
|
|
60869a0490 | ||
|
|
102be5fa2e | ||
|
|
a25c04b390 | ||
|
|
d46f0a1c33 | ||
|
|
5c7f5a99e1 | ||
|
|
d949a80f19 | ||
|
|
6b5d4e2027 | ||
|
|
199d071ee8 | ||
|
|
5b78de5d61 | ||
|
|
db4f13e2e1 | ||
|
|
5e869b7e03 | ||
|
|
34fee36132 | ||
|
|
ea09bc36a5 | ||
|
|
f7e0678cb2 | ||
|
|
223ae378a9 | ||
|
|
6f52a48991 | ||
|
|
0280935955 | ||
|
|
eebc7d9ab3 | ||
|
|
aa7b5652ff | ||
|
|
ec6683a198 | ||
|
|
05372924a0 | ||
|
|
e63c1399dd | ||
|
|
4fcad92e1d | ||
|
|
e18e7298bc | ||
|
|
76a6a509cd | ||
|
|
7a03561ff6 | ||
|
|
70e54ce14a | ||
|
|
b5f86f0e79 | ||
|
|
4ebca49ef7 | ||
|
|
49cfa95463 | ||
|
|
4600ab0593 | ||
|
|
7f2ca98bde | ||
|
|
6511485bcf | ||
|
|
fd22113e44 | ||
|
|
f2ae078723 | ||
|
|
c3dacf2b9b | ||
|
|
e8580dc1d5 | ||
|
|
daffff7eb0 | ||
|
|
751fda5e37 | ||
|
|
1e38abc500 | ||
|
|
8e7137815b | ||
|
|
4226ee453f | ||
|
|
568a110443 | ||
|
|
7fd7a6ca8d | ||
|
|
3092c854ff | ||
|
|
a9566f4b23 | ||
|
|
7a5aa3c11d | ||
|
|
f2454bb028 | ||
|
|
2c1023a6fd | ||
|
|
9187ac6684 | ||
|
|
9640e43bc5 | ||
|
|
09b24b36f2 | ||
|
|
06a761c0d0 | ||
|
|
6ff7d7fcd7 | ||
|
|
0f27c79303 | ||
|
|
9961ad081f | ||
|
|
02056b4f78 | ||
|
|
0492588c25 | ||
|
|
ad8463ac8b | ||
|
|
7843de240a | ||
|
|
0037a08017 | ||
|
|
92ba432992 | ||
|
|
3a521f4014 | ||
|
|
e8bbc9799c | ||
|
|
96a48b4813 | ||
|
|
355450e82d | ||
|
|
99307c5b0c | ||
|
|
ace8be6514 | ||
|
|
1b538931b1 | ||
|
|
7ccd08ee2b | ||
|
|
104beed594 | ||
|
|
e8df594edb | ||
|
|
6e922a84f8 | ||
|
|
b74bf86277 | ||
|
|
6cbe50c9ef | ||
|
|
14fcca040b | ||
|
|
7bb545cff9 | ||
|
|
f52b1a246c | ||
|
|
273dfd89cc | ||
|
|
52d72d287b | ||
|
|
e8d8fcbe69 | ||
|
|
30c6c1afd3 | ||
|
|
c8c8093b3a | ||
|
|
c970b2cf2e | ||
|
|
0815283d31 | ||
|
|
55ee2d93b8 | ||
|
|
c9e17ed42b | ||
|
|
76a91b7fe0 | ||
|
|
f9b3169b6a | ||
|
|
4f42b5a3ee | ||
|
|
5d9645eaff | ||
|
|
c032808d82 | ||
|
|
282c94266e | ||
|
|
e2f7d52bee | ||
|
|
6317f0162a | ||
|
|
436a6310d7 | ||
|
|
0f74195255 | ||
|
|
5ba33786ab | ||
|
|
70fcfc1753 | ||
|
|
1e34048c0c | ||
|
|
45b1b23262 | ||
|
|
07842ace4e | ||
|
|
8e75280093 | ||
|
|
029b9ef498 | ||
|
|
51970abce7 | ||
|
|
f6faa19e5f | ||
|
|
dd7217ca54 | ||
|
|
6e13457eb2 | ||
|
|
3b319ee19b | ||
|
|
8ea8daa811 | ||
|
|
eb29b908c2 | ||
|
|
9935cb482e | ||
|
|
0d6e7181cf | ||
|
|
d225650e15 | ||
|
|
bfe09791d5 | ||
|
|
85d103f3f6 | ||
|
|
9e6dbe2465 | ||
|
|
355e5ccda6 | ||
|
|
a5f6f05e10 | ||
|
|
db0d35d80b | ||
|
|
6fca2a3931 | ||
|
|
0305a42b02 | ||
|
|
77d81716ed | ||
|
|
1fc8ee6fee | ||
|
|
3e54e80eb6 | ||
|
|
3c9bb63d32 | ||
|
|
617ab9efab | ||
|
|
bc574097e2 | ||
|
|
4cc64dc233 | ||
|
|
45fa0a165a | ||
|
|
0e5c07a078 | ||
|
|
601a742c71 | ||
|
|
c972401b6e | ||
|
|
024e81cf01 | ||
|
|
975ed402d5 | ||
|
|
b9e8083744 | ||
|
|
bb859708bc | ||
|
|
3cf2c221ac | ||
|
|
6edd7edcd2 | ||
|
|
4e26a02d78 | ||
|
|
bb36a57053 | ||
|
|
b291c800f1 | ||
|
|
b63a798d86 | ||
|
|
eacf3777a4 | ||
|
|
a5a7436bb1 | ||
|
|
2a4cc35df7 | ||
|
|
cdccf58b76 | ||
|
|
27300383a1 | ||
|
|
375b9bce30 | ||
|
|
b6b8db48df | ||
|
|
36e6c267b9 | ||
|
|
13066b3b4a | ||
|
|
ccbe9d00c8 | ||
|
|
ce291582cb | ||
|
|
bb37bc3b51 | ||
|
|
5a7747acd1 | ||
|
|
1bc2ec9461 | ||
|
|
2b977fc2b0 | ||
|
|
de60c4ee9e | ||
|
|
de67e40c00 | ||
|
|
cc4b016c64 | ||
|
|
f64f561d6f | ||
|
|
80bddf8a6b | ||
|
|
cbaaec961c | ||
|
|
5477b3f936 | ||
|
|
fd59a93ede | ||
|
|
cd316b7138 | ||
|
|
d1955192ed | ||
|
|
9beb839bf4 | ||
|
|
29ea303093 | ||
|
|
feff6751ca | ||
|
|
ca33fdf752 | ||
|
|
fdcdfe89d6 | ||
|
|
48ed2115a7 | ||
|
|
65988f4e08 | ||
|
|
ede65eda6c | ||
|
|
5da56acac8 | ||
|
|
5720c55301 | ||
|
|
ffefa4b30e | ||
|
|
80e4986b23 | ||
|
|
dc92ccda0a | ||
|
|
f9bab18076 | ||
|
|
2dec52e221 | ||
|
|
7413f693d7 | ||
|
|
415460df75 | ||
|
|
125a194468 | ||
|
|
32e9afbf36 | ||
|
|
3eca704f4a | ||
|
|
9c95129311 | ||
|
|
bf34c1bcdb | ||
|
|
284c687d77 | ||
|
|
09afb5a3f5 | ||
|
|
0138721451 | ||
|
|
2d5f610941 | ||
|
|
864fa7762b | ||
|
|
4fde38ee6a | ||
|
|
6cdf0f10d4 | ||
|
|
b66592c25f | ||
|
|
43616c566d | ||
|
|
62f1dc17a0 | ||
|
|
0e9a8a937a | ||
|
|
9a86b245ce | ||
|
|
64533f7a3f | ||
|
|
0b7de8d387 | ||
|
|
8eba4860fe | ||
|
|
b53e2f57e6 | ||
|
|
e1e834297b | ||
|
|
e37dc6e341 | ||
|
|
c91c896854 | ||
|
|
7e5dfa03d6 | ||
|
|
1a4ec3f049 | ||
|
|
756763fcbe | ||
|
|
93036c4e67 | ||
|
|
15bf972ef6 | ||
|
|
bcb4567382 | ||
|
|
3890c4ffb9 | ||
|
|
c8d82f24a8 | ||
|
|
367e740a9c | ||
|
|
5b5a51bdad | ||
|
|
7fc030a53e | ||
|
|
65087fb5f8 | ||
|
|
d5e789af3c | ||
|
|
45212c699f | ||
|
|
03106f3522 | ||
|
|
cd2857cb94 | ||
|
|
b3ddb8658f | ||
|
|
7ae80b6b49 | ||
|
|
34f27f9cd4 | ||
|
|
4c53b58fd8 | ||
|
|
6102ab4be8 | ||
|
|
6fcf565974 | ||
|
|
77bd16c7cf | ||
|
|
7d593080ec | ||
|
|
96ea74abeb | ||
|
|
e50e3957ec | ||
|
|
8cb9420939 | ||
|
|
bbb7b8caa2 | ||
|
|
cc284f3c9e | ||
|
|
bd8aae5fbf | ||
|
|
3d162f33df | ||
|
|
ff3c37f1b4 | ||
|
|
35160ce385 | ||
|
|
c8eef91813 | ||
|
|
6791b20121 | ||
|
|
3311c49236 | ||
|
|
5223e58f83 | ||
|
|
e0f9fc233d | ||
|
|
509bd21655 | ||
|
|
5be7229c4b | ||
|
|
efd3c461e0 | ||
|
|
13aed69f0e | ||
|
|
b03675811c | ||
|
|
e4beac185e | ||
|
|
bdc7d9dd84 | ||
|
|
b49d1e0529 | ||
|
|
3c0eb48d53 | ||
|
|
0d65531ec1 | ||
|
|
3848f9822b | ||
|
|
e1cdc002b9 | ||
|
|
168c1cf1ce | ||
|
|
5b142b4401 | ||
|
|
9be3d76590 | ||
|
|
26971aa109 | ||
|
|
18f9049bc6 | ||
|
|
53cf253b67 | ||
|
|
6d416f45a9 | ||
|
|
a34a447164 | ||
|
|
d3247a9ec1 | ||
|
|
dfb5a2b97f | ||
|
|
deae96e191 | ||
|
|
b8c0e18bb4 | ||
|
|
a083da1546 | ||
|
|
2518c70ec8 | ||
|
|
6ead8d261c | ||
|
|
b5f76a6ebc | ||
|
|
5e38bca535 | ||
|
|
c39142ee77 | ||
|
|
d0174479d7 | ||
|
|
aa23267d70 | ||
|
|
92f5da306d | ||
|
|
8601e2af7a | ||
|
|
5d9f31b99e | ||
|
|
8b29158d8b | ||
|
|
b68cf3c671 | ||
|
|
858a50a414 | ||
|
|
598df624df | ||
|
|
e6da2b73ed | ||
|
|
f44424d37a | ||
|
|
623804ae68 | ||
|
|
b7544939c2 | ||
|
|
63fa6d4660 | ||
|
|
9506c11951 | ||
|
|
ce5639bb98 | ||
|
|
99ef6ca861 | ||
|
|
43d5a3da27 | ||
|
|
30723110af | ||
|
|
a0ecb4e2bb | ||
|
|
a726b09f26 | ||
|
|
52b1d1fe33 | ||
|
|
8b4709c132 | ||
|
|
b4217a791e | ||
|
|
9b3a982d54 | ||
|
|
0a47d3eab5 | ||
|
|
7d7803247d | ||
|
|
aa8d45e636 | ||
|
|
7f3e87767c | ||
|
|
f76ea9bba5 | ||
|
|
1eefeae060 | ||
|
|
03447c33be | ||
|
|
f6c6cd5cb8 | ||
|
|
67b56cee50 | ||
|
|
2d7465cca6 | ||
|
|
d06f20690d | ||
|
|
e613a494ff | ||
|
|
503a01675e | ||
|
|
723f7e63e9 | ||
|
|
7ff6916e5f | ||
|
|
910ed26525 | ||
|
|
c0fffa6293 | ||
|
|
d800d3ba41 | ||
|
|
d267343c64 | ||
|
|
0f72e37272 | ||
|
|
89e44d581f | ||
|
|
c908a710de | ||
|
|
f0809cde72 | ||
|
|
e6da8c1c18 | ||
|
|
81ea67afa8 | ||
|
|
488049821e | ||
|
|
fc7408a8f8 | ||
|
|
6955191fdd | ||
|
|
ba59693d90 | ||
|
|
ce6e51128d | ||
|
|
a7a26037ae | ||
|
|
00854abd1a | ||
|
|
c26649d5f2 | ||
|
|
63b76a249f | ||
|
|
95f19208ad | ||
|
|
f4a902ee47 | ||
|
|
036823cbb6 | ||
|
|
547c2c8c0d | ||
|
|
7453d19d27 | ||
|
|
2a0b68d637 | ||
|
|
02f374fdf0 | ||
|
|
3536dcd775 | ||
|
|
6fa887e536 | ||
|
|
71755763b9 | ||
|
|
5aabcccfd4 | ||
|
|
1e009e2211 | ||
|
|
811bc6434b | ||
|
|
a5b2d06742 | ||
|
|
2c00674835 | ||
|
|
9bbff1f436 | ||
|
|
c75416f9be | ||
|
|
2cb750a7b0 | ||
|
|
abfec51964 | ||
|
|
e314717a3e | ||
|
|
290a78acb5 | ||
|
|
b40a4dd243 | ||
|
|
b38f6d0581 | ||
|
|
5c562a6bdb | ||
|
|
238d43f916 | ||
|
|
818dc23089 | ||
|
|
ddb50940ce | ||
|
|
96fb97f4ef | ||
|
|
6c0459c2ae | ||
|
|
30a9e1ee4b | ||
|
|
15a1f1e6e0 | ||
|
|
827e2b161c | ||
|
|
e95230859e | ||
|
|
95cb8ca493 | ||
|
|
5735293efb | ||
|
|
035df1f338 | ||
|
|
36a8b04c4f | ||
|
|
3d5411ef28 | ||
|
|
df7b74e727 | ||
|
|
0138599120 | ||
|
|
66446cbffc | ||
|
|
ef40f3900f | ||
|
|
ce259764a8 | ||
|
|
65187cb17b | ||
|
|
8f25245ad1 | ||
|
|
58b8baa019 | ||
|
|
ba1db88c8d | ||
|
|
c8f2f42a35 | ||
|
|
4e5a9b2af5 | ||
|
|
d1ab961c14 | ||
|
|
33caf4644e | ||
|
|
f34ffe18a8 | ||
|
|
4b1c5e111d | ||
|
|
dbd7c296c6 | ||
|
|
df93cbde8f | ||
|
|
d8cbc121a7 | ||
|
|
8163cd5c8f | ||
|
|
e9e9adb174 | ||
|
|
21fdfc1eef | ||
|
|
4304251e1e | ||
|
|
ae0cd74b0e | ||
|
|
6935bff244 | ||
|
|
102b9da617 | ||
|
|
8bd0d185ae | ||
|
|
7bfc5ae0a8 | ||
|
|
c72a41db9d | ||
|
|
5fbb1c2e32 | ||
|
|
850dd53c69 | ||
|
|
e3baf91037 | ||
|
|
4c811c9c04 | ||
|
|
4bc9f5826e | ||
|
|
9f19e4dc83 | ||
|
|
c9cb865c9c | ||
|
|
b8a7dd984d | ||
|
|
3ede46c445 | ||
|
|
87c4af94d3 | ||
|
|
6c30bd36d1 | ||
|
|
7e85ccaf28 | ||
|
|
db3a262410 | ||
|
|
70162da491 | ||
|
|
f3632a4033 | ||
|
|
2f4e176116 | ||
|
|
3ea8dc598c | ||
|
|
7987083555 | ||
|
|
e94e195e73 | ||
|
|
869ebd90df |
1
.flutter
Submodule
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -9,6 +9,7 @@ assignees: ''
|
|||||||
|
|
||||||
**Prerequisites**
|
**Prerequisites**
|
||||||
<!-- Please ensure your request is not part of an existing issue. -->
|
<!-- Please ensure your request is not part of an existing issue. -->
|
||||||
|
<!-- Please ensure you have checked the Obtainium Wiki. -->
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
<!-- A clear and concise description of what the bug is. -->
|
<!-- A clear and concise description of what the bug is. -->
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -9,6 +9,7 @@ assignees: ''
|
|||||||
|
|
||||||
**Prerequisites**
|
**Prerequisites**
|
||||||
<!-- Please ensure your request is not part of an existing issue. -->
|
<!-- Please ensure your request is not part of an existing issue. -->
|
||||||
|
<!-- Please ensure you have checked the Obtainium Wiki. -->
|
||||||
|
|
||||||
**Describe the feature**
|
**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.
|
||||||
|
|||||||
88
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
name: Build and Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
beta:
|
||||||
|
type: boolean
|
||||||
|
description: Is beta?
|
||||||
|
|
||||||
|
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: Check submodule
|
||||||
|
id: check_submodule
|
||||||
|
run: |
|
||||||
|
SUBMODULE_COMMIT_LONG="$(git submodule status | head -1 | tail -c +2 | awk '{print $1}')"
|
||||||
|
FLUTTER_COMMIT_SHORT="$(flutter --version | head -2 | tail -1 | awk '{print $4}')"
|
||||||
|
echo "SUBMODULE_COMMIT_LONG=$SUBMODULE_COMMIT_LONG, FLUTTER_COMMIT_SHORT=$FLUTTER_COMMIT_SHORT"
|
||||||
|
if ! [[ "$SUBMODULE_COMMIT_LONG" =~ ^$FLUTTER_COMMIT_SHORT ]]; then
|
||||||
|
echo "Your submodule has not been updated!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Extract Version
|
||||||
|
id: extract_version
|
||||||
|
run: |
|
||||||
|
VERSION=$(grep -oP "^version: [^\+]+" pubspec.yaml | tail -c +10)
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
if [ ${{ inputs.beta }} == true ]; then BETA=true; else BETA=false; fi
|
||||||
|
echo "beta=$BETA" >> $GITHUB_OUTPUT
|
||||||
|
TAG="v$VERSION"
|
||||||
|
if [ $BETA == true ]; then TAG="$TAG"-beta; fi
|
||||||
|
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Build APKs
|
||||||
|
run: |
|
||||||
|
sed -i 's/signingConfig signingConfigs.release//g' android/app/build.gradle
|
||||||
|
flutter build apk --flavor normal && flutter build apk --split-per-abi --flavor normal
|
||||||
|
for file in build/app/outputs/flutter-apk/app-*normal*.apk*; do mv "$file" "${file//-normal/}"; done
|
||||||
|
flutter build apk --flavor fdroid -t lib/main_fdroid.dart && flutter build apk --split-per-abi --flavor fdroid -t lib/main_fdroid.dart
|
||||||
|
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/$(ls ${ANDROID_HOME}/build-tools/ | tail -1)/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: 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
|
||||||
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule ".flutter"]
|
||||||
|
path = .flutter
|
||||||
|
url = https://github.com/flutter/flutter/
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
#  Obtainium
|
#  Obtainium
|
||||||
|
|
||||||
|
[](https://techforpalestine.org/learn-more)
|
||||||
|
|
||||||
Get Android App Updates Directly From the Source.
|
Get Android App Updates Directly From the Source.
|
||||||
|
|
||||||
Obtainium allows you to install and update 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)
|
Motivation: [Side Of Burritos - You should use this instead of F-Droid | How to use app RSS feed](https://youtu.be/FFz57zNR_M0)
|
||||||
|
|
||||||
|
Read the Wiki: [https://github.com/ImranR98/Obtainium/wiki](https://github.com/ImranR98/Obtainium/wiki)
|
||||||
|
|
||||||
Currently supported App sources:
|
Currently supported App sources:
|
||||||
- Open Source - General:
|
- Open Source - General:
|
||||||
- [GitHub](https://github.com/)
|
- [GitHub](https://github.com/)
|
||||||
@@ -17,7 +21,7 @@ Currently supported App sources:
|
|||||||
- [SourceForge](https://sourceforge.net/)
|
- [SourceForge](https://sourceforge.net/)
|
||||||
- [SourceHut](https://git.sr.ht/)
|
- [SourceHut](https://git.sr.ht/)
|
||||||
- Other - General:
|
- Other - General:
|
||||||
- [APKPure](https://apkpure.com/)
|
- [APKPure](https://apkpure.net/)
|
||||||
- [Aptoide](https://aptoide.com/)
|
- [Aptoide](https://aptoide.com/)
|
||||||
- [Uptodown](https://uptodown.com/)
|
- [Uptodown](https://uptodown.com/)
|
||||||
- [APKMirror](https://apkmirror.com/) (Track-Only)
|
- [APKMirror](https://apkmirror.com/) (Track-Only)
|
||||||
@@ -28,8 +32,8 @@ Currently supported App sources:
|
|||||||
- [Signal](https://signal.org/)
|
- [Signal](https://signal.org/)
|
||||||
- [VLC](https://videolan.org/)
|
- [VLC](https://videolan.org/)
|
||||||
- Other - App-Specific:
|
- Other - App-Specific:
|
||||||
|
- [WhatsApp](https://whatsapp.com)
|
||||||
- [Telegram App](https://telegram.org)
|
- [Telegram App](https://telegram.org)
|
||||||
- [Steam Mobile Apps](https://store.steampowered.com/mobile)
|
|
||||||
- [Neutron Code](https://neutroncode.com)
|
- [Neutron Code](https://neutroncode.com)
|
||||||
- "HTML" (Fallback): Any other URL that returns an HTML page with links to APK files
|
- "HTML" (Fallback): Any other URL that returns an HTML page with links to APK files
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ if (flutterVersionName == null) {
|
|||||||
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'dev.rikka.tools.refine'
|
||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
def keystoreProperties = new Properties()
|
def keystoreProperties = new Properties()
|
||||||
@@ -32,7 +33,7 @@ if (keystorePropertiesFile.exists()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 33
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@@ -52,12 +53,25 @@ android {
|
|||||||
applicationId "dev.imranr.obtainium"
|
applicationId "dev.imranr.obtainium"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
||||||
minSdkVersion 23
|
minSdkVersion 24
|
||||||
targetSdkVersion 33
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flavorDimensions "flavor"
|
||||||
|
|
||||||
|
productFlavors {
|
||||||
|
normal {
|
||||||
|
dimension "flavor"
|
||||||
|
applicationIdSuffix ""
|
||||||
|
}
|
||||||
|
fdroid {
|
||||||
|
dimension "flavor"
|
||||||
|
applicationIdSuffix ".fdroid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
release {
|
release {
|
||||||
keyAlias keystoreProperties['keyAlias']
|
keyAlias keystoreProperties['keyAlias']
|
||||||
@@ -77,6 +91,33 @@ flutter {
|
|||||||
source '../..'
|
source '../..'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
repositories {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
maven { url 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
def shizuku_version = '13.1.5'
|
||||||
|
implementation "dev.rikka.shizuku:api:$shizuku_version"
|
||||||
|
implementation "dev.rikka.shizuku:provider:$shizuku_version"
|
||||||
|
|
||||||
|
def hidden_api_version = '4.3.1'
|
||||||
|
implementation "dev.rikka.tools.refine:runtime:$hidden_api_version"
|
||||||
|
implementation "dev.rikka.hidden:compat:$hidden_api_version"
|
||||||
|
compileOnly "dev.rikka.hidden:stub:$hidden_api_version"
|
||||||
|
implementation "org.lsposed.hiddenapibypass:hiddenapibypass:4.3"
|
||||||
|
|
||||||
|
implementation "com.github.topjohnwu.libsu:core:5.2.2"
|
||||||
|
}
|
||||||
|
|
||||||
|
ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3]
|
||||||
|
import com.android.build.OutputFile
|
||||||
|
android.applicationVariants.all { variant ->
|
||||||
|
variant.outputs.each { output ->
|
||||||
|
def abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
|
||||||
|
if (abiVersionCode != null) {
|
||||||
|
output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode
|
||||||
|
} else {
|
||||||
|
output.versionCodeOverride = variant.versionCode * 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="dev.imranr.obtainium">
|
package="dev.imranr.obtainium">
|
||||||
<application
|
<application
|
||||||
android:label="Obtainium"
|
android:label="Obtainium"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:requestLegacyExternalStorage="true">
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:usesCleartextTraffic="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@@ -28,48 +30,46 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action
|
<action
|
||||||
android:name="com.android_package_installer.content.SESSION_API_PACKAGE_INSTALLED"
|
android:name="com.android_package_installer.content.SESSION_API_PACKAGE_INSTALLED"
|
||||||
android:exported="false"/>
|
android:exported="false" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="obtainium" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
<service
|
|
||||||
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmService"
|
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
|
||||||
android:exported="false"/>
|
|
||||||
<receiver
|
|
||||||
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmBroadcastReceiver"
|
|
||||||
android:exported="false"/>
|
|
||||||
<receiver
|
|
||||||
android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
|
|
||||||
android:enabled="false"
|
|
||||||
android:exported="false">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="dev.imranr.obtainium"
|
android:authorities="dev.imranr.obtainium"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/file_paths"/>
|
android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
<provider
|
||||||
|
android:name="rikka.shizuku.ShizukuProvider"
|
||||||
|
android:authorities="${applicationId}.shizuku"
|
||||||
|
android:multiprocess="false"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||||
</application>
|
</application>
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
|
|
||||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="29"/>
|
android:maxSdkVersion="29" />
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package dev.imranr.obtainium
|
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
|
||||||
|
|
||||||
class MainActivity: FlutterActivity() {
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package dev.imranr.obtainium
|
||||||
|
|
||||||
|
import android.util.Xml
|
||||||
|
import org.xmlpull.v1.XmlPullParser
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
|
||||||
|
class DefaultSystemFont {
|
||||||
|
fun get(): String {
|
||||||
|
return try {
|
||||||
|
val file = File("/system/etc/fonts.xml")
|
||||||
|
val fileStream = FileInputStream(file)
|
||||||
|
parseFontsFileStream(fileStream)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.message ?: "Unknown fonts.xml parsing exception"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseFontsFileStream(fileStream: FileInputStream): String {
|
||||||
|
fileStream.use { stream ->
|
||||||
|
val parser = Xml.newPullParser()
|
||||||
|
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
|
||||||
|
parser.setInput(stream, null)
|
||||||
|
parser.nextTag()
|
||||||
|
return parseFonts(parser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseFonts(parser: XmlPullParser): String {
|
||||||
|
while (!((parser.next() == XmlPullParser.END_TAG) && (parser.name == "family"))) {
|
||||||
|
if ((parser.eventType == XmlPullParser.START_TAG) && (parser.name == "font")
|
||||||
|
&& (parser.getAttributeValue(null, "style") == "normal")
|
||||||
|
&& (parser.getAttributeValue(null, "weight") == "400")) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parser.next()
|
||||||
|
val fontFile = parser.text.trim()
|
||||||
|
if (fontFile == "") {
|
||||||
|
throw NoSuchFieldException("The font filename couldn't be found in fonts.xml")
|
||||||
|
}
|
||||||
|
return "/system/fonts/$fontFile"
|
||||||
|
}
|
||||||
|
}
|
||||||
179
android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
package dev.imranr.obtainium
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentSender
|
||||||
|
import android.content.pm.IPackageInstaller
|
||||||
|
import android.content.pm.IPackageInstallerSession
|
||||||
|
import android.content.pm.PackageInstaller
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Process
|
||||||
|
import androidx.annotation.NonNull
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import dev.imranr.obtainium.util.IIntentSenderAdaptor
|
||||||
|
import dev.imranr.obtainium.util.IntentSenderUtils
|
||||||
|
import dev.imranr.obtainium.util.PackageInstallerUtils
|
||||||
|
import dev.imranr.obtainium.util.ShizukuSystemServerApi
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import io.flutter.plugin.common.MethodChannel.Result
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
import org.lsposed.hiddenapibypass.HiddenApiBypass
|
||||||
|
import rikka.shizuku.Shizuku
|
||||||
|
import rikka.shizuku.Shizuku.OnRequestPermissionResultListener
|
||||||
|
import rikka.shizuku.ShizukuBinderWrapper
|
||||||
|
|
||||||
|
class MainActivity: FlutterActivity() {
|
||||||
|
private var nativeChannel: MethodChannel? = null
|
||||||
|
private val SHIZUKU_PERMISSION_REQUEST_CODE = (10..200).random()
|
||||||
|
|
||||||
|
private fun shizukuCheckPermission(result: Result) {
|
||||||
|
try {
|
||||||
|
if (Shizuku.isPreV11()) { // Unsupported
|
||||||
|
result.success(-1)
|
||||||
|
} else if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
result.success(1)
|
||||||
|
} else if (Shizuku.shouldShowRequestPermissionRationale()) { // Deny and don't ask again
|
||||||
|
result.success(0)
|
||||||
|
} else {
|
||||||
|
Shizuku.requestPermission(SHIZUKU_PERMISSION_REQUEST_CODE)
|
||||||
|
result.success(-2)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) { // If shizuku not running
|
||||||
|
result.success(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val shizukuRequestPermissionResultListener = OnRequestPermissionResultListener {
|
||||||
|
requestCode: Int, grantResult: Int ->
|
||||||
|
if (requestCode == SHIZUKU_PERMISSION_REQUEST_CODE) {
|
||||||
|
val res = if (grantResult == PackageManager.PERMISSION_GRANTED) 1 else 0
|
||||||
|
nativeChannel!!.invokeMethod("resPermShizuku", mapOf("res" to res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shizukuInstallApk(apkFileUri: String, result: Result) {
|
||||||
|
val uri = Uri.parse(apkFileUri)
|
||||||
|
var res = false
|
||||||
|
var session: PackageInstaller.Session? = null
|
||||||
|
try {
|
||||||
|
val iPackageInstaller: IPackageInstaller =
|
||||||
|
ShizukuSystemServerApi.PackageManager_getPackageInstaller()
|
||||||
|
val isRoot = Shizuku.getUid() == 0
|
||||||
|
// The reason for use "com.android.shell" as installer package under adb
|
||||||
|
// is that getMySessions will check installer package's owner
|
||||||
|
val installerPackageName = if (isRoot) packageName else "com.android.shell"
|
||||||
|
var installerAttributionTag: String? = null
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
installerAttributionTag = attributionTag
|
||||||
|
}
|
||||||
|
val userId = if (isRoot) Process.myUserHandle().hashCode() else 0
|
||||||
|
val packageInstaller = PackageInstallerUtils.createPackageInstaller(
|
||||||
|
iPackageInstaller, installerPackageName, installerAttributionTag, userId)
|
||||||
|
val params =
|
||||||
|
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||||
|
var installFlags: Int = PackageInstallerUtils.getInstallFlags(params)
|
||||||
|
installFlags = installFlags or (0x00000002/*PackageManager.INSTALL_REPLACE_EXISTING*/
|
||||||
|
or 0x00000004 /*PackageManager.INSTALL_ALLOW_TEST*/)
|
||||||
|
PackageInstallerUtils.setInstallFlags(params, installFlags)
|
||||||
|
val sessionId = packageInstaller.createSession(params)
|
||||||
|
val iSession = IPackageInstallerSession.Stub.asInterface(
|
||||||
|
ShizukuBinderWrapper(iPackageInstaller.openSession(sessionId).asBinder()))
|
||||||
|
session = PackageInstallerUtils.createSession(iSession)
|
||||||
|
val inputStream = contentResolver.openInputStream(uri)
|
||||||
|
val openedSession = session.openWrite("apk.apk", 0, -1)
|
||||||
|
val buffer = ByteArray(8192)
|
||||||
|
var length: Int
|
||||||
|
try {
|
||||||
|
while (inputStream!!.read(buffer).also { length = it } > 0) {
|
||||||
|
openedSession.write(buffer, 0, length)
|
||||||
|
openedSession.flush()
|
||||||
|
session.fsync(openedSession)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
inputStream!!.close()
|
||||||
|
openedSession.close()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val results = arrayOf<Intent?>(null)
|
||||||
|
val countDownLatch = CountDownLatch(1)
|
||||||
|
val intentSender: IntentSender =
|
||||||
|
IntentSenderUtils.newInstance(object : IIntentSenderAdaptor() {
|
||||||
|
override fun send(intent: Intent?) {
|
||||||
|
results[0] = intent
|
||||||
|
countDownLatch.countDown()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
session.commit(intentSender)
|
||||||
|
countDownLatch.await()
|
||||||
|
res = results[0]!!.getIntExtra(
|
||||||
|
PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE) == 0
|
||||||
|
} catch (_: Exception) {
|
||||||
|
res = false
|
||||||
|
} finally {
|
||||||
|
if (session != null) {
|
||||||
|
try {
|
||||||
|
session.close()
|
||||||
|
} catch (_: Exception) {
|
||||||
|
res = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.success(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rootCheckPermission(result: Result) {
|
||||||
|
Shell.getShell(Shell.GetShellCallback(
|
||||||
|
fun(shell: Shell) {
|
||||||
|
result.success(shell.isRoot)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rootInstallApk(apkFilePath: String, result: Result) {
|
||||||
|
Shell.sh("pm install -r -t " + apkFilePath).submit { out ->
|
||||||
|
val builder = StringBuilder()
|
||||||
|
for (data in out.getOut()) { builder.append(data) }
|
||||||
|
result.success(builder.toString().endsWith("Success"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||||
|
super.configureFlutterEngine(flutterEngine)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
HiddenApiBypass.addHiddenApiExemptions("")
|
||||||
|
}
|
||||||
|
Shizuku.addRequestPermissionResultListener(shizukuRequestPermissionResultListener)
|
||||||
|
nativeChannel = MethodChannel(
|
||||||
|
flutterEngine.dartExecutor.binaryMessenger, "native")
|
||||||
|
nativeChannel!!.setMethodCallHandler {
|
||||||
|
call, result ->
|
||||||
|
if (call.method == "getSystemFont") {
|
||||||
|
val res = DefaultSystemFont().get()
|
||||||
|
result.success(res)
|
||||||
|
} else if (call.method == "checkPermissionShizuku") {
|
||||||
|
shizukuCheckPermission(result)
|
||||||
|
} else if (call.method == "checkPermissionRoot") {
|
||||||
|
rootCheckPermission(result)
|
||||||
|
} else if (call.method == "installWithShizuku") {
|
||||||
|
val apkFileUri: String? = call.argument("apkFileUri")
|
||||||
|
shizukuInstallApk(apkFileUri!!, result)
|
||||||
|
} else if (call.method == "installWithRoot") {
|
||||||
|
val apkFilePath: String? = call.argument("apkFilePath")
|
||||||
|
rootInstallApk(apkFilePath!!, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
Shizuku.removeRequestPermissionResultListener(shizukuRequestPermissionResultListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package dev.imranr.obtainium.util;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class ApplicationUtils {
|
||||||
|
|
||||||
|
private static Application application;
|
||||||
|
|
||||||
|
public static Application getApplication() {
|
||||||
|
return application;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setApplication(Application application) {
|
||||||
|
ApplicationUtils.application = application;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getProcessName() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 28)
|
||||||
|
return Application.getProcessName();
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
@SuppressLint("PrivateApi")
|
||||||
|
Class<?> activityThread = Class.forName("android.app.ActivityThread");
|
||||||
|
@SuppressLint("DiscouragedPrivateApi")
|
||||||
|
Method method = activityThread.getDeclaredMethod("currentProcessName");
|
||||||
|
return (String) method.invoke(null);
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package dev.imranr.obtainium.util;
|
||||||
|
|
||||||
|
import android.content.IIntentReceiver;
|
||||||
|
import android.content.IIntentSender;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
public abstract class IIntentSenderAdaptor extends IIntentSender.Stub {
|
||||||
|
|
||||||
|
public abstract void send(Intent intent);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
|
||||||
|
send(intent);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
|
||||||
|
send(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package dev.imranr.obtainium.util;
|
||||||
|
|
||||||
|
import android.content.IIntentSender;
|
||||||
|
import android.content.IntentSender;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
public class IntentSenderUtils {
|
||||||
|
|
||||||
|
public static IntentSender newInstance(IIntentSender binder) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||||
|
//noinspection JavaReflectionMemberAccess
|
||||||
|
return IntentSender.class.getConstructor(IIntentSender.class).newInstance(binder);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package dev.imranr.obtainium.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.IPackageInstaller;
|
||||||
|
import android.content.pm.IPackageInstallerSession;
|
||||||
|
import android.content.pm.PackageInstaller;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
@SuppressWarnings({"JavaReflectionMemberAccess"})
|
||||||
|
public class PackageInstallerUtils {
|
||||||
|
|
||||||
|
public static PackageInstaller createPackageInstaller(IPackageInstaller installer, String installerPackageName, String installerAttributionTag, int userId) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
return PackageInstaller.class.getConstructor(IPackageInstaller.class, String.class, String.class, int.class)
|
||||||
|
.newInstance(installer, installerPackageName, installerAttributionTag, userId);
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
return PackageInstaller.class.getConstructor(IPackageInstaller.class, String.class, int.class)
|
||||||
|
.newInstance(installer, installerPackageName, userId);
|
||||||
|
} else {
|
||||||
|
return PackageInstaller.class.getConstructor(Context.class, PackageManager.class, IPackageInstaller.class, String.class, int.class)
|
||||||
|
.newInstance(ApplicationUtils.getApplication(), ApplicationUtils.getApplication().getPackageManager(), installer, installerPackageName, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PackageInstaller.Session createSession(IPackageInstallerSession session) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||||
|
return PackageInstaller.Session.class.getConstructor(IPackageInstallerSession.class)
|
||||||
|
.newInstance(session);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getInstallFlags(PackageInstaller.SessionParams params) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
return (int) PackageInstaller.SessionParams.class.getDeclaredField("installFlags").get(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setInstallFlags(PackageInstaller.SessionParams params, int newValue) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
PackageInstaller.SessionParams.class.getDeclaredField("installFlags").set(params, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package dev.imranr.obtainium.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.IPackageInstaller;
|
||||||
|
import android.content.pm.IPackageManager;
|
||||||
|
import android.content.pm.UserInfo;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.IUserManager;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import rikka.shizuku.ShizukuBinderWrapper;
|
||||||
|
import rikka.shizuku.SystemServiceHelper;
|
||||||
|
|
||||||
|
public class ShizukuSystemServerApi {
|
||||||
|
|
||||||
|
private static final Singleton<IPackageManager> PACKAGE_MANAGER = new Singleton<IPackageManager>() {
|
||||||
|
@Override
|
||||||
|
protected IPackageManager create() {
|
||||||
|
return IPackageManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final Singleton<IUserManager> USER_MANAGER = new Singleton<IUserManager>() {
|
||||||
|
@Override
|
||||||
|
protected IUserManager create() {
|
||||||
|
return IUserManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService(Context.USER_SERVICE)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static IPackageInstaller PackageManager_getPackageInstaller() throws RemoteException {
|
||||||
|
IPackageInstaller packageInstaller = PACKAGE_MANAGER.get().getPackageInstaller();
|
||||||
|
return IPackageInstaller.Stub.asInterface(new ShizukuBinderWrapper(packageInstaller.asBinder()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<UserInfo> UserManager_getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated) throws RemoteException {
|
||||||
|
if (Build.VERSION.SDK_INT >= 30) {
|
||||||
|
return USER_MANAGER.get().getUsers(excludePartial, excludeDying, excludePreCreated);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return USER_MANAGER.get().getUsers(excludeDying);
|
||||||
|
} catch (NoSuchFieldError e) {
|
||||||
|
return USER_MANAGER.get().getUsers(excludePartial, excludeDying, excludePreCreated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// method 2: use transactRemote directly
|
||||||
|
/*public static List<UserInfo> UserManager_getUsers(boolean excludeDying) {
|
||||||
|
Parcel data = SystemServiceHelper.obtainParcel(Context.USER_SERVICE, "android.os.IUserManager", "getUsers");
|
||||||
|
Parcel reply = Parcel.obtain();
|
||||||
|
data.writeInt(excludeDying ? 1 : 0);
|
||||||
|
|
||||||
|
List<UserInfo> res = null;
|
||||||
|
try {
|
||||||
|
ShizukuService.transactRemote(data, reply, 0);
|
||||||
|
reply.readException();
|
||||||
|
res = reply.createTypedArrayList(UserInfo.CREATOR);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e("ShizukuSample", "UserManager#getUsers", e);
|
||||||
|
} finally {
|
||||||
|
data.recycle();
|
||||||
|
reply.recycle();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package dev.imranr.obtainium.util;
|
||||||
|
|
||||||
|
public abstract class Singleton<T> {
|
||||||
|
|
||||||
|
private T mInstance;
|
||||||
|
|
||||||
|
protected abstract T create();
|
||||||
|
|
||||||
|
public final T get() {
|
||||||
|
synchronized (this) {
|
||||||
|
if (mInstance == null) {
|
||||||
|
mInstance = create();
|
||||||
|
}
|
||||||
|
return mInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
android:viewportWidth="142.129"
|
android:viewportWidth="142.129"
|
||||||
android:viewportHeight="142.129"
|
android:viewportHeight="142.129"
|
||||||
android:width="503.6066dp"
|
android:width="108dp"
|
||||||
android:height="503.6066dp">
|
android:height="108dp">
|
||||||
<group
|
<group
|
||||||
android:translateX="-30.39437"
|
android:translateX="-30.39437"
|
||||||
android:translateY="-54.68043">
|
android:translateY="-54.68043">
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.7.10'
|
ext.kotlin_version = '1.8.10'
|
||||||
|
ext {
|
||||||
|
compileSdkVersion = 34 // or latest
|
||||||
|
targetSdkVersion = 34 // or latest
|
||||||
|
appCompatVersion = "1.4.2" // or latest
|
||||||
|
}
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.2.0'
|
classpath "com.android.tools.build:gradle:7.4.2"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
classpath "dev.rikka.tools.refine:gradle-plugin:4.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,6 +21,10 @@ allprojects {
|
|||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
// [required] background_fetch
|
||||||
|
url "${project(':background_fetch').projectDir}/libs"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
assets/graphics/badge_obtainium.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
@@ -1,315 +1,355 @@
|
|||||||
{
|
{
|
||||||
"invalidURLForSource": "Nije važeći URL aplikacije {}",
|
"invalidURLForSource": "Nije važeći URL aplikacije {}",
|
||||||
"noReleaseFound": "Nije moguće pronaći odgovarajuće izdanje",
|
"noReleaseFound": "Nije moguće pronaći odgovarajuće izdanje",
|
||||||
"noVersionFound": "Nije moguće odrediti verziju izdanja",
|
"noVersionFound": "Nije moguće odrediti verziju izdanja",
|
||||||
"urlMatchesNoSource": "URL se ne podudara s poznatim izvorom",
|
"urlMatchesNoSource": "URL se ne podudara s poznatim izvorom",
|
||||||
"cantInstallOlderVersion": "Nije moguće instalirati stariju verziju aplikacije",
|
"cantInstallOlderVersion": "Nije moguće instalirati stariju verziju aplikacije",
|
||||||
"appIdMismatch": "ID preuzetog paketa se ne podudara s postojećim ID-om aplikacije",
|
"appIdMismatch": "ID preuzetog paketa se ne podudara s postojećim ID-om aplikacije",
|
||||||
"functionNotImplemented": "Ova klasa nije implementirala ovu funkciju",
|
"functionNotImplemented": "Ova klasa nije implementirala ovu funkciju",
|
||||||
"placeholder": "Rezervirano mjesto",
|
"placeholder": "Rezervirano mjesto",
|
||||||
"someErrors": "Došlo je do nekih grešaka",
|
"someErrors": "Došlo je do nekih grešaka",
|
||||||
"unexpectedError": "Neočekivana greška",
|
"unexpectedError": "Neočekivana greška",
|
||||||
"ok": "Dobro",
|
"ok": "Dobro",
|
||||||
"and": "i",
|
"and": "i",
|
||||||
"githubPATLabel": "GitHub token za lični pristup (eng. PAT, povećava ograničenje stope)",
|
"githubPATLabel": "GitHub token za lični pristup (eng. PAT, povećava ograničenje stope)",
|
||||||
"includePrereleases": "Uključi preliminarna izdanja",
|
"includePrereleases": "Uključi preliminarna izdanja",
|
||||||
"fallbackToOlderReleases": "Povratak na starija izdanja",
|
"fallbackToOlderReleases": "Povratak na starija izdanja",
|
||||||
"filterReleaseTitlesByRegEx": "Filtrirajte naslove izdanja prema regularnom izrazu",
|
"filterReleaseTitlesByRegEx": "Filtrirajte naslove izdanja prema regularnom izrazu",
|
||||||
"invalidRegEx": "Nevažeći regularni izraz",
|
"invalidRegEx": "Nevažeći regularni izraz",
|
||||||
"noDescription": "Bez opisa",
|
"noDescription": "Bez opisa",
|
||||||
"cancel": "Otkaži",
|
"cancel": "Otkaži",
|
||||||
"continue": "Nastavite",
|
"continue": "Nastavite",
|
||||||
"requiredInBrackets": "(obavezno)",
|
"requiredInBrackets": "(obavezno)",
|
||||||
"dropdownNoOptsError": "GREŠKA: PADAJUĆI MENI MORA IMATI NAJMANJE JEDNU OPCIJU",
|
"dropdownNoOptsError": "GREŠKA: PADAJUĆI MENI MORA IMATI NAJMANJE JEDNU OPCIJU",
|
||||||
"colour": "Boja",
|
"colour": "Boja",
|
||||||
"githubStarredRepos": "GitHub repo-i sa zvjezdicom",
|
"githubStarredRepos": "GitHub repo-i sa zvjezdicom",
|
||||||
"uname": "Korisničko ime",
|
"uname": "Korisničko ime",
|
||||||
"wrongArgNum": "Naveden je pogrešan broj argumenata",
|
"wrongArgNum": "Naveden je pogrešan broj argumenata",
|
||||||
"xIsTrackOnly": "{} je samo za praćenje",
|
"xIsTrackOnly": "{} je samo za praćenje",
|
||||||
"source": "Izvor",
|
"source": "Izvor",
|
||||||
"app": "Aplikacija. ",
|
"app": "Aplikacija. ",
|
||||||
"appsFromSourceAreTrackOnly": "Aplikacije iz ovog izvora su 'Samo za praćenje'.",
|
"appsFromSourceAreTrackOnly": "Aplikacije iz ovog izvora su 'Samo za praćenje'.",
|
||||||
"youPickedTrackOnly": "Odabrali ste opciju „Samo za praćenje”.",
|
"youPickedTrackOnly": "Odabrali ste opciju „Samo za praćenje”.",
|
||||||
"trackOnlyAppDescription": "Aplikacija će se pratiti radi ažuriranja, ali Obtainium neće moći da je preuzme ili instalira.",
|
"trackOnlyAppDescription": "Aplikacija će se pratiti radi ažuriranja, ali Obtainium neće moći da je preuzme ili instalira.",
|
||||||
"cancelled": "Otkazano",
|
"cancelled": "Otkazano",
|
||||||
"appAlreadyAdded": "Aplikacija je već dodana",
|
"appAlreadyAdded": "Aplikacija je već dodana",
|
||||||
"alreadyUpToDateQuestion": "Aplikacija je već ažurirana?",
|
"alreadyUpToDateQuestion": "Aplikacija je već ažurirana?",
|
||||||
"addApp": "Dodaj aplikaciju",
|
"addApp": "Dodaj aplikaciju",
|
||||||
"appSourceURL": "Izvorni URL aplikacije",
|
"appSourceURL": "Izvorni URL aplikacije",
|
||||||
"error": "Greška",
|
"error": "Greška",
|
||||||
"add": "Dodaj",
|
"add": "Dodaj",
|
||||||
"searchSomeSourcesLabel": "Pretraživanje (samo neki izvori)",
|
"searchSomeSourcesLabel": "Pretraživanje (samo neki izvori)",
|
||||||
"search": "Pretraživanje",
|
"search": "Pretraživanje",
|
||||||
"additionalOptsFor": "Dodatne opcije za {}",
|
"additionalOptsFor": "Dodatne opcije za {}",
|
||||||
"supportedSources": "Podržani izvori",
|
"supportedSources": "Podržani izvori",
|
||||||
"trackOnlyInBrackets": "(Samo za praćenje)",
|
"trackOnlyInBrackets": "(Samo za praćenje)",
|
||||||
"searchableInBrackets": "(Može se pretraživati)",
|
"searchableInBrackets": "(Može se pretraživati)",
|
||||||
"appsString": "Aplikacije",
|
"appsString": "Aplikacije",
|
||||||
"noApps": "Nema aplikacija",
|
"noApps": "Nema aplikacija",
|
||||||
"noAppsForFilter": "Nema aplikacija za filter",
|
"noAppsForFilter": "Nema aplikacija za filter",
|
||||||
"byX": "Autor {}",
|
"byX": "Autor {}",
|
||||||
"percentProgress": "Napredak: {}%",
|
"percentProgress": "Napredak: {}%",
|
||||||
"pleaseWait": "Molimo sačekajte",
|
"pleaseWait": "Molimo sačekajte",
|
||||||
"updateAvailable": "Ažuriranje dostupno",
|
"updateAvailable": "Ažuriranje dostupno",
|
||||||
"estimateInBracketsShort": "(Procjena)",
|
"notInstalled": "Nije instalirano",
|
||||||
"notInstalled": "Nije instalirano",
|
"pseudoVersion": "pseudo-verzija",
|
||||||
"estimateInBrackets": "(Procjena)",
|
"selectAll": "Označi sve",
|
||||||
"selectAll": "Označi sve",
|
"deselectX": "Poništi odabir {}",
|
||||||
"deselectN": "Poništi odabir {}",
|
"xWillBeRemovedButRemainInstalled": "{} će biti uklonjen iz Obtainiuma, ali će ostati instaliran na uređaju.",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} će biti uklonjen iz Obtainiuma, ali će ostati instaliran na uređaju.",
|
"removeSelectedAppsQuestion": "Želite li ukloniti odabrane aplikacije?",
|
||||||
"removeSelectedAppsQuestion": "Želite li ukloniti odabrane aplikacije?",
|
"removeSelectedApps": "Ukloni odabrane aplikacije",
|
||||||
"removeSelectedApps": "Ukloni odabrane aplikacije",
|
"updateX": "Nadogradi {}",
|
||||||
"updateX": "Nadogradi {}",
|
"installX": "Instaliraj {}",
|
||||||
"installX": "Instaliraj {}",
|
"markXTrackOnlyAsUpdated": "Označi {}\n(samo za praćenje)\nkao ažurirano",
|
||||||
"markXTrackOnlyAsUpdated": "Označi {}\n(samo za praćenje)\nkao ažurirano",
|
"changeX": "Promjena {}",
|
||||||
"changeX": "Promjena {}",
|
"installUpdateApps": "Instalirajte/ažurirajte aplikacije",
|
||||||
"installUpdateApps": "Instalirajte/ažurirajte aplikacije",
|
"installUpdateSelectedApps": "Instalirajte/ažurirajte odabrane aplikacije",
|
||||||
"installUpdateSelectedApps": "Instalirajte/ažurirajte odabrane aplikacije",
|
"markXSelectedAppsAsUpdated": "Označite {} odabrane aplikacije kao ažurirane?",
|
||||||
"markXSelectedAppsAsUpdated": "Označite {} odabrane aplikacije kao ažurirane?",
|
"no": "Ne",
|
||||||
"no": "Ne",
|
"yes": "Da",
|
||||||
"yes": "Da",
|
"markSelectedAppsUpdated": "Označi odabrane aplikacije kao ažurirane",
|
||||||
"markSelectedAppsUpdated": "Označi odabrane aplikacije kao ažurirane",
|
"pinToTop": "Prikvači na vrh",
|
||||||
"pinToTop": "Prikvači na vrh",
|
"unpinFromTop": "Otkvači sa vrha",
|
||||||
"unpinFromTop": "Otkvači sa vrha",
|
"resetInstallStatusForSelectedAppsQuestion": "Resetujte status instalacije za odabrane aplikacije?",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "Resetujte status instalacije za odabrane aplikacije?",
|
"installStatusOfXWillBeResetExplanation": "Status instalacije bilo koje odabrane aplikacije će se resetovati.\n\nTo može pomoći kada je verzija aplikacije prikazana u Obtainiumu netačna zbog neuspjelih ažuriranja ili drugih problema.",
|
||||||
"installStatusOfXWillBeResetExplanation": "Status instalacije bilo koje odabrane aplikacije će se resetovati.\n\nTo može pomoći kada je verzija aplikacije prikazana u Obtainiumu netačna zbog neuspjelih ažuriranja ili drugih problema.",
|
"customLinkMessage": "Ove veze rade na uređajima s instaliranim Obtainiumom",
|
||||||
"shareSelectedAppURLs": "Podijeli odabrane URL-ove aplikacija",
|
"shareAppConfigLinks": "Podijelite konfiguraciju aplikacije kao HTML vezu",
|
||||||
"resetInstallStatus": "Resetujte status instalacije",
|
"shareSelectedAppURLs": "Podijeli odabrane URL-ove aplikacija",
|
||||||
"more": "Više",
|
"resetInstallStatus": "Resetujte status instalacije",
|
||||||
"removeOutdatedFilter": "Uklonite zastarjeli filter aplikacija",
|
"more": "Više",
|
||||||
"showOutdatedOnly": "Prikaži samo zastarjele aplikacije",
|
"removeOutdatedFilter": "Uklonite zastarjeli filter aplikacija",
|
||||||
"filter": "Filtriranje",
|
"showOutdatedOnly": "Prikaži samo zastarjele aplikacije",
|
||||||
"filterActive": "Filtriranje",
|
"filter": "Filtriranje",
|
||||||
"filterApps": "Filtriraj aplikacije",
|
"filterActive": "Filtriranje",
|
||||||
"appName": "Naziv aplikacije",
|
"filterApps": "Filtriraj aplikacije",
|
||||||
"author": "Autor",
|
"appName": "Naziv aplikacije",
|
||||||
"upToDateApps": "Ažurirane aplikacije",
|
"author": "Autor",
|
||||||
"nonInstalledApps": "Neinstalirane aplikacije",
|
"upToDateApps": "Ažurirane aplikacije",
|
||||||
"importExport": "Uvoz/izvoz",
|
"nonInstalledApps": "Neinstalirane aplikacije",
|
||||||
"settings": "Postavke",
|
"importExport": "Uvoz/izvoz",
|
||||||
"exportedTo": "Izvezeno u {}",
|
"settings": "Postavke",
|
||||||
"obtainiumExport": "Obtainium Export",
|
"exportedTo": "Izvezeno u {}",
|
||||||
"invalidInput": "Neispravan unos.",
|
"obtainiumExport": "Obtainium izvoz",
|
||||||
"importedX": "Uvezeno {}",
|
"invalidInput": "Neispravan unos.",
|
||||||
"obtainiumImport": "Obtainium uvoz",
|
"importedX": "Uvezeno {}",
|
||||||
"importFromURLList": "Uvoz iz URL liste",
|
"obtainiumImport": "Obtainium uvoz",
|
||||||
"searchQuery": "Pretraga za: ",
|
"importFromURLList": "Uvoz iz URL liste",
|
||||||
"appURLList": "Lista URL adresa aplikacija",
|
"searchQuery": "Pretraga za: ",
|
||||||
"line": "Linija",
|
"appURLList": "Lista URL adresa aplikacija",
|
||||||
"searchX": "Pretraživanje {}",
|
"line": "Linija",
|
||||||
"noResults": "Nema rezultata",
|
"searchX": "Pretraživanje {}",
|
||||||
"importX": "Uvoz {}",
|
"noResults": "Nema rezultata",
|
||||||
"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.",
|
"importX": "Uvoz {}",
|
||||||
"importErrors": "Uvezi greške",
|
"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.",
|
||||||
"importedXOfYApps": "{} od {} aplikacija uvezeno.",
|
"importErrors": "Uvezi greške",
|
||||||
"followingURLsHadErrors": "Sljedeći URL-ovi su imali greške:",
|
"importedXOfYApps": "{} od {} aplikacija uvezeno.",
|
||||||
"okay": "Dobro",
|
"followingURLsHadErrors": "Sljedeći URL-ovi su imali greške:",
|
||||||
"selectURL": "Odaberite URL",
|
"selectURL": "Odaberite URL",
|
||||||
"selectURLs": "Odaberite URL-ove",
|
"selectURLs": "Odaberite URL-ove",
|
||||||
"pick": "Odaberi",
|
"pick": "Odaberi",
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"dark": "Tamna",
|
"dark": "Tamna",
|
||||||
"light": "Svijetla",
|
"light": "Svijetla",
|
||||||
"followSystem": "Pratite sistem",
|
"followSystem": "Pratite sistem",
|
||||||
"obtainium": "Obtainium",
|
"useBlackTheme": "Koristite čisto crnu tamnu temu",
|
||||||
"materialYou": "Material You",
|
"appSortBy": "Aplikacije sortirane po",
|
||||||
"useBlackTheme": "Koristite čisto crnu tamnu temu",
|
"authorName": "Autor/Ime",
|
||||||
"appSortBy": "Aplikacije sortirane po",
|
"nameAuthor": "Ime/Autor",
|
||||||
"authorName": "Autor/Ime",
|
"asAdded": "Kao što je dodano",
|
||||||
"nameAuthor": "Ime/Autor",
|
"appSortOrder": "Redoslijed sortiranja aplikacija",
|
||||||
"asAdded": "Kao što je dodano",
|
"ascending": "Uzlazno",
|
||||||
"appSortOrder": "Redoslijed sortiranja aplikacija",
|
"descending": "Silazno",
|
||||||
"ascending": "Uzlazno",
|
"bgUpdateCheckInterval": "Interval provjere ažuriranja u pozadini",
|
||||||
"descending": "Silazno",
|
"neverManualOnly": "Nikada - samo ručno",
|
||||||
"bgUpdateCheckInterval": "Interval provjere ažuriranja u pozadini",
|
"appearance": "Izgled",
|
||||||
"neverManualOnly": "Nikada - samo ručno",
|
"showWebInAppView": "Prikaži izvornu web stranicu u prikazu aplikacije",
|
||||||
"appearance": "Izgled",
|
"pinUpdates": "Prikvačite ažuriranja na vrh prikaza aplikacija",
|
||||||
"showWebInAppView": "Prikaži izvornu web stranicu u prikazu aplikacije",
|
"updates": "Nadogradnje",
|
||||||
"pinUpdates": "Prikvačite ažuriranja na vrh prikaza aplikacija",
|
"sourceSpecific": "Specifično za izvor",
|
||||||
"updates": "Nadogradnje",
|
"appSource": "Izvor aplikacije",
|
||||||
"sourceSpecific": "Specifično za izvor",
|
"noLogs": "Nema evidencije",
|
||||||
"appSource": "Izvor aplikacije",
|
"appLogs": "Evidencije aplikacija",
|
||||||
"noLogs": "Nema evidencije",
|
"close": "Zatvori",
|
||||||
"appLogs": "Evidencije aplikacija",
|
"share": "Podijeli",
|
||||||
"close": "Zatvori",
|
"appNotFound": "Aplikacija nije pronađena",
|
||||||
"share": "Podijeli",
|
"obtainiumExportHyphenatedLowercase": "obtainium-izvoz",
|
||||||
"appNotFound": "Aplikacija nije pronađena",
|
"pickAnAPK": "Odaberite APK",
|
||||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
"appHasMoreThanOnePackage": "{} ima više od jednog paketa:",
|
||||||
"pickAnAPK": "Odaberite APK",
|
"deviceSupportsXArch": "Vaš uređaj podržava {} arhitekturu procesora.",
|
||||||
"appHasMoreThanOnePackage": "{} ima više od jednog paketa:",
|
"deviceSupportsFollowingArchs": "Vaš uređaj podržava sljedeće arhitekture procesora:",
|
||||||
"deviceSupportsXArch": "Vaš uređaj podržava {} arhitekturu procesora.",
|
"warning": "Upozorenje",
|
||||||
"deviceSupportsFollowingArchs": "Vaš uređaj podržava sljedeće arhitekture procesora:",
|
"sourceIsXButPackageFromYPrompt": "Izvor aplikacije je '{}', ali paket za izdavanje dolazi iz '{}'. Želite li nastaviti?",
|
||||||
"warning": "Upozorenje",
|
"updatesAvailable": "Dostupna ažuriranja",
|
||||||
"sourceIsXButPackageFromYPrompt": "Izvor aplikacije je '{}', ali paket za izdavanje dolazi iz '{}'. Želite li nastaviti?",
|
"updatesAvailableNotifDescription": "Obavještava korisnika da su ažuriranja dostupna za jednu ili više aplikacija koje prati Obtainium",
|
||||||
"updatesAvailable": "Dostupna ažuriranja",
|
"noNewUpdates": "Nema novih ažuriranja.",
|
||||||
"updatesAvailableNotifDescription": "Obavještava korisnika da su ažuriranja dostupna za jednu ili više aplikacija koje prati Obtainium",
|
"xHasAnUpdate": "{} ima ažuriranje.",
|
||||||
"noNewUpdates": "Nema novih ažuriranja.",
|
"appsUpdated": "Aplikacije su ažurirane",
|
||||||
"xHasAnUpdate": "{} ima ažuriranje.",
|
"appsUpdatedNotifDescription": "Obavještava korisnika da su u pozadini primijenjena ažuriranja na jednu ili više aplikacija",
|
||||||
"appsUpdated": "Aplikacije su ažurirane",
|
"xWasUpdatedToY": "{} je ažuriran na {}.",
|
||||||
"appsUpdatedNotifDescription": "Obavještava korisnika da su u pozadini primijenjena ažuriranja na jednu ili više aplikacija",
|
"errorCheckingUpdates": "Greška pri provjeri ažuriranja",
|
||||||
"xWasUpdatedToY": "{} je ažuriran na {}.",
|
"errorCheckingUpdatesNotifDescription": "Obavijest koja se prikazuje kada provjera sigurnosnog ažuriranja ne uspije",
|
||||||
"errorCheckingUpdates": "Greška pri provjeri ažuriranja",
|
"appsRemoved": "Aplikacije su uklonjene",
|
||||||
"errorCheckingUpdatesNotifDescription": "Obavijest koja se prikazuje kada provjera sigurnosnog ažuriranja ne uspije",
|
"appsRemovedNotifDescription": "Obavještava korisnika da je jedna ili više aplikacija uklonjeno zbog grešaka prilikom učitavanja",
|
||||||
"appsRemoved": "Aplikacije su uklonjene",
|
"xWasRemovedDueToErrorY": "{} je uklonjen zbog ove greške: {}",
|
||||||
"appsRemovedNotifDescription": "Obavještava korisnika da je jedna ili više aplikacija uklonjeno zbog grešaka prilikom učitavanja",
|
"completeAppInstallation": "Dovršite instalaciju aplikacije",
|
||||||
"xWasRemovedDueToErrorY": "{} je uklonjen zbog ove greške: {}",
|
"obtainiumMustBeOpenToInstallApps": "Obtainium mora biti otvoren za instalaciju aplikacija",
|
||||||
"completeAppInstallation": "Dovršite instalaciju aplikacije",
|
"completeAppInstallationNotifDescription": "Traži od korisnika da se vrati u Obtainium kako bi dovršio instalaciju aplikacije",
|
||||||
"obtainiumMustBeOpenToInstallApps": "Obtainium mora biti otvoren za instalaciju aplikacija",
|
"checkingForUpdates": "Tražim moguće nadogradnje",
|
||||||
"completeAppInstallationNotifDescription": "Traži od korisnika da se vrati u Obtainium kako bi dovršio instalaciju aplikacije",
|
"checkingForUpdatesNotifDescription": "Privremeno obavještenje koje se pojavljuje prilikom provjere ažuriranja",
|
||||||
"checkingForUpdates": "Tražim moguće nadogradnje",
|
"pleaseAllowInstallPerm": "Dozvolite Obtainiumu da instalira aplikacije",
|
||||||
"checkingForUpdatesNotifDescription": "Privremeno obavještenje koje se pojavljuje prilikom provjere ažuriranja",
|
"trackOnly": "Samo za praćenje",
|
||||||
"pleaseAllowInstallPerm": "Dozvolite Obtainiumu da instalira aplikacije",
|
"errorWithHttpStatusCode": "Greška {}",
|
||||||
"trackOnly": "Samo za praćenje",
|
"versionCorrectionDisabled": "Ispravka verzije je onemogućena (izgleda da plugin ne radi)",
|
||||||
"errorWithHttpStatusCode": "Greška {}",
|
"unknown": "Nepoznato",
|
||||||
"versionCorrectionDisabled": "Ispravka verzije je onemogućena (izgleda da plugin ne radi)",
|
"none": "Ništa",
|
||||||
"unknown": "Nepoznato",
|
"never": "Nikad",
|
||||||
"none": "Ništa",
|
"latestVersionX": "Najnovija verzija: {}",
|
||||||
"never": "Nikad",
|
"installedVersionX": "Instalirana verzija: {}",
|
||||||
"latestVersionX": "Najnovija verzija: {}",
|
"lastUpdateCheckX": "Posljednja provjera ažuriranja: {}",
|
||||||
"installedVersionX": "Instalirana verzija: {}",
|
"remove": "Izbriši",
|
||||||
"lastUpdateCheckX": "Posljednja provjera ažuriranja: {}",
|
"yesMarkUpdated": "Da, označi kao ažurirano",
|
||||||
"remove": "Izbriši",
|
"fdroid": "Službeni F-Droid",
|
||||||
"yesMarkUpdated": "Da, označi kao ažurirano",
|
"appIdOrName": "ID ili ime aplikacije",
|
||||||
"fdroid": "F-Droid Official",
|
"appId": "Apl ID",
|
||||||
"appIdOrName": "ID ili ime aplikacije",
|
"appWithIdOrNameNotFound": "Nije pronađena aplikacija s tim ID-om ili imenom",
|
||||||
"appId": "Apl ID",
|
"reposHaveMultipleApps": "Repo-i mogu sadržavati više aplikacija",
|
||||||
"appWithIdOrNameNotFound": "Nije pronađena aplikacija s tim ID-om ili imenom",
|
"fdroidThirdPartyRepo": "F-Droid Repo treće strane",
|
||||||
"reposHaveMultipleApps": "Repo-i mogu sadržavati više aplikacija",
|
"steamMobile": "Steam Mobile",
|
||||||
"fdroidThirdPartyRepo": "F-Droid Repo treće strane",
|
"steamChat": "Razgovor na Steamu (chat)",
|
||||||
"steam": "Steam",
|
"install": "Instaliraj",
|
||||||
"steamMobile": "Steam Mobile",
|
"markInstalled": "Označi kao instalirano",
|
||||||
"steamChat": "Razgovor na Steamu (chat)",
|
"update": "Nadogradi",
|
||||||
"install": "Instaliraj",
|
"markUpdated": "Označi kao ažurirano",
|
||||||
"markInstalled": "Označi kao instalirano",
|
"additionalOptions": "Dodatne opcije",
|
||||||
"update": "Nadogradi",
|
"disableVersionDetection": "Onemogući detekciju verzije",
|
||||||
"markUpdated": "Označi kao ažurirano",
|
"noVersionDetectionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno.",
|
||||||
"additionalOptions": "Dodatne opcije",
|
"downloadingX": "Preuzimanje {}",
|
||||||
"disableVersionDetection": "Onemogući detekciju verzije",
|
"downloadNotifDescription": "Obavještava korisnika o napretku u preuzimanju aplikacije",
|
||||||
"noVersionDetectionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno.",
|
"noAPKFound": "APK nije pronađen",
|
||||||
"downloadingX": "Preuzimanje {}",
|
"noVersionDetection": "Nema detekcije verzije",
|
||||||
"downloadNotifDescription": "Obavještava korisnika o napretku u preuzimanju aplikacije",
|
"categorize": "Kategoriziraj",
|
||||||
"noAPKFound": "APK nije pronađen",
|
"categories": "Kategorije",
|
||||||
"noVersionDetection": "Nema detekcije verzije",
|
"category": "Kategorija",
|
||||||
"categorize": "Kategoriziraj",
|
"noCategory": "Nema kategorije",
|
||||||
"categories": "Kategorije",
|
"noCategories": "Nema kategorija",
|
||||||
"category": "Kategorija",
|
"deleteCategoriesQuestion": "Želite li izbrisati kategorije?",
|
||||||
"noCategory": "Nema kategorije",
|
"categoryDeleteWarning": "Sve aplikacije u izbrisanim kategorijama će biti postavljene kao nekategorisane.",
|
||||||
"noCategories": "Nema kategorija",
|
"addCategory": "Dodaj kategoriju",
|
||||||
"deleteCategoriesQuestion": "Želite li izbrisati kategorije?",
|
"label": "Oznaka",
|
||||||
"categoryDeleteWarning": "Sve aplikacije u izbrisanim kategorijama će biti postavljene kao nekategorisane.",
|
"language": "Jezik",
|
||||||
"addCategory": "Dodaj kategoriju",
|
"copiedToClipboard": "Podaci kopirani u međuspremnik",
|
||||||
"label": "Oznaka",
|
"storagePermissionDenied": "Dozvola za pohranu je odbijena",
|
||||||
"language": "Jezik",
|
"selectedCategorizeWarning": "Ovo će zamijeniti sve postojeće postavke kategorije za odabrane aplikacije.",
|
||||||
"copiedToClipboard": "Podaci kopirani u međuspremnik",
|
"filterAPKsByRegEx": "Filtrirajte APK-ove prema regularnom izrazu",
|
||||||
"storagePermissionDenied": "Dozvola za pohranu je odbijena",
|
"removeFromObtainium": "Ukloni iz Obtainiuma",
|
||||||
"selectedCategorizeWarning": "Ovo će zamijeniti sve postojeće postavke kategorije za odabrane aplikacije.",
|
"uninstallFromDevice": "Deinstaliraj s uređaja",
|
||||||
"filterAPKsByRegEx": "Filtrirajte APK-ove prema regularnom izrazu",
|
"onlyWorksWithNonVersionDetectApps": "Radi samo za aplikacije s onemogućenom detekcijom verzije.",
|
||||||
"removeFromObtainium": "Ukloni iz Obtainiuma",
|
"releaseDateAsVersion": "Koristi datum izdanja kao verziju",
|
||||||
"uninstallFromDevice": "Deinstaliraj s uređaja",
|
"releaseDateAsVersionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno, ali je datum izdavanja dostupan.",
|
||||||
"onlyWorksWithNonVersionDetectApps": "Radi samo za aplikacije s onemogućenom detekcijom verzije.",
|
"changes": "Promjene",
|
||||||
"releaseDateAsVersion": "Koristi datum izdanja kao verziju",
|
"releaseDate": "Datum izdavanja",
|
||||||
"releaseDateAsVersionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno, ali je datum izdavanja dostupan.",
|
"importFromURLsInFile": "Uvoz iz URL-ova u datoteci (kao što je OPML)",
|
||||||
"changes": "Promjene",
|
"versionDetectionExplanation": "Pomiri niz verzije sa verzijom otkrivenom iz OS-a",
|
||||||
"releaseDate": "Datum izdavanja",
|
"versionDetection": "Otkrivanje verzije",
|
||||||
"importFromURLsInFile": "Uvoz iz URL-ova u datoteci (kao što je OPML)",
|
"standardVersionDetection": "Detekcija standardne verzije",
|
||||||
"versionDetection": "Otkrivanje verzije",
|
"groupByCategory": "Grupiši po kategoriji",
|
||||||
"standardVersionDetection": "Detekcija standardne verzije",
|
"autoApkFilterByArch": "Pokušajte filtrirati APK-ove po arhitekturi procesora ako je moguće",
|
||||||
"groupByCategory": "Grupiši po kategoriji",
|
"overrideSource": "Premosti izvor",
|
||||||
"autoApkFilterByArch": "Pokušajte filtrirati APK-ove po arhitekturi procesora ako je moguće",
|
"dontShowAgain": "Ne prikazuj ovo ponovo",
|
||||||
"overrideSource": "Premosti izvor",
|
"dontShowTrackOnlyWarnings": "Ne prikazuj upozorenja „Samo za praćenje”",
|
||||||
"dontShowAgain": "Ne prikazuj ovo ponovo",
|
"dontShowAPKOriginWarnings": "Ne prikazuj upozorenja o porijeklu APK-a",
|
||||||
"dontShowTrackOnlyWarnings": "Ne prikazuj upozorenja „Samo za praćenje”",
|
"moveNonInstalledAppsToBottom": "Premjesti neinstalirane aplikacije na dno prikaza aplikacija",
|
||||||
"dontShowAPKOriginWarnings": "Ne prikazuj upozorenja o porijeklu APK-a",
|
"gitlabPATLabel": "GitLab token za lični pristup\n(Omogućava pretraživanje i bolje otkrivanje APK-a)",
|
||||||
"moveNonInstalledAppsToBottom": "Premjesti neinstalirane aplikacije na dno prikaza aplikacija",
|
"about": "O nama",
|
||||||
"gitlabPATLabel": "GitLab token za lični pristup\n(Omogućava pretraživanje i bolje otkrivanje APK-a)",
|
"requiresCredentialsInSettings": "{}: Za ovo su potrebni dodatni akreditivi (u Postavkama)",
|
||||||
"about": "O nama",
|
"checkOnStart": "Provjerite ima li novosti pri pokretanju",
|
||||||
"requiresCredentialsInSettings": "Za ovo su potrebni dodatni akreditivi (u Postavkama)",
|
"tryInferAppIdFromCode": "Pokušati otkriti ID aplikacije iz izvornog koda",
|
||||||
"checkOnStart": "Provjerite ima li novosti pri pokretanju",
|
"removeOnExternalUninstall": "Automatski ukloni eksterno deinstalirane aplikacije",
|
||||||
"tryInferAppIdFromCode": "Pokušati otkriti ID aplikacije iz izvornog koda",
|
"pickHighestVersionCode": "Automatski odaberite najviši kôd verzije APK-a",
|
||||||
"removeOnExternalUninstall": "Automatski ukloni eksterno deinstalirane aplikacije",
|
"checkUpdateOnDetailPage": "Provjerite ima li novosti pri otvaranju stranice s detaljima aplikacije",
|
||||||
"pickHighestVersionCode": "Automatski odaberite najviši kôd verzije APK-a",
|
"disablePageTransitions": "Ugasite animaciju prijelaza stranice",
|
||||||
"checkUpdateOnDetailPage": "Provjerite ima li novosti pri otvaranju stranice s detaljima aplikacije",
|
"reversePageTransitions": "Reverzne animacije prijelaza stranice",
|
||||||
"disablePageTransitions": "Ugasite animaciju prijelaza stranice",
|
"minStarCount": "Najmanji broj zvjezdica",
|
||||||
"reversePageTransitions": "Reverzne animacije prijelaza stranice",
|
"addInfoBelow": "Dodajte ove informacije ispod.",
|
||||||
"minStarCount": "Minimum Star Count",
|
"addInfoInSettings": "Dodajte ove informacije u Postavkama.",
|
||||||
"addInfoBelow": "Add this info below.",
|
"githubSourceNote": "GitHub ograničavanje se može izbjeći korišćenjem tokena za lični pristup.",
|
||||||
"addInfoInSettings": "Add this info in the Settings.",
|
"gitlabSourceNote": "GitLab APK preuzimanje možda neće raditi bez tokena za lični pristup.",
|
||||||
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
|
"sortByLastLinkSegment": "Sortiraj samo po zadnjem segmentu veze",
|
||||||
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
|
"filterReleaseNotesByRegEx": "Filtirajte promjene u izdanju po regularnom izrazu",
|
||||||
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
|
"customLinkFilterRegex": "Prilagođeni APK link filtrira se po regularnom izrazu (Zadano '.apk$')",
|
||||||
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
|
"appsPossiblyUpdated": "Pokušano ažuriranje aplikacija",
|
||||||
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
|
"appsPossiblyUpdatedNotifDescription": "Obavještava korisnika da je ažuriranje jedne ili više aplikacija potencijalno izvršeno u pozadini",
|
||||||
"appsPossiblyUpdated": "App Updates Attempted",
|
"xWasPossiblyUpdatedToY": "{} aplikacija bi trebala biti ažurirana na {}.",
|
||||||
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
|
"enableBackgroundUpdates": "Dozvolite ažuriranja u pozadini",
|
||||||
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
|
"backgroundUpdateReqsExplanation": "Ažuriranja u pozadini možda neće raditi za sve aplikacije.",
|
||||||
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
|
"backgroundUpdateLimitsExplanation": "Uspjeh ažuriranja u pozadini se može provjeriti tek kada otvorite Obtainium.",
|
||||||
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
|
"verifyLatestTag": "Provjerite 'posljednu' ('latest') oznaku",
|
||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"intermediateLinkRegex": "Filter za 'srednju' vezu za posjetu",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"filterByLinkText": "Filtriraj linkove prema tekstu linka",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"intermediateLinkNotFound": "Intermediate link nije nađen",
|
||||||
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
"intermediateLink": "srednja karika",
|
||||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
"exemptFromBackgroundUpdates": "Izuzmi iz ažuriranja u pozadini (ako su uključeni)",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"bgUpdatesOnWiFiOnly": "Isključite ažuriranje u pozadini kada niste na WiFi-ju",
|
||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"autoSelectHighestVersionCode": "Automatski izaberite najveću (verziju) versionCode APK-a",
|
||||||
"pickExportDir": "Pick Export Directory",
|
"versionExtractionRegEx": "RegEx ekstrakcija verzije",
|
||||||
"autoExportOnChanges": "Auto-export on changes",
|
"matchGroupToUse": "Podjesite grupu za upotebu",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"highlightTouchTargets": "Istaknite manje vidljive touch mete",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"pickExportDir": "Izaberite datoteku za izvoz",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"autoExportOnChanges": "Automatski izvezite pri promjenama",
|
||||||
"reverseSort": "Reverse sorting",
|
"includeSettings": "Uključi postavke",
|
||||||
"debugMenu": "Debug Menu",
|
"filterVersionsByRegEx": "Filtrirajte verzije po regulatnom izrazu",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"trySelectingSuggestedVersionCode": "Probajte izabrati preloženu (verziju) versionCode APK-a",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"dontSortReleasesList": "Zadrži redosled izdanja iz API-a",
|
||||||
"removeAppQuestion": {
|
"reverseSort": "Obrni redosled",
|
||||||
"one": "Želite li ukloniti aplikaciju?",
|
"takeFirstLink": "Uzmi prvi link",
|
||||||
"other": "Želite li ukloniti aplikacije?"
|
"skipSort": "Preskoči sortiranje",
|
||||||
},
|
"debugMenu": "Meni za otkrivanje grešaka",
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"bgTaskStarted": "Rad u pozadini pokrenut - provjerite log-ove.",
|
||||||
"one": "Previše zahtjeva (ograničena broj zahteva) - pokušajte ponovo za {} minutu",
|
"runBgCheckNow": "Pokrenite pozadinsku provjeru ažuriranja sad",
|
||||||
"other": "Previše zahtjeva (ograničena cijena) - pokušajte ponovo za {} min."
|
"versionExtractWholePage": "Primjenite Regex ekstrakciju verzije na cijelu stranicu",
|
||||||
},
|
"installing": "Instaliranje",
|
||||||
"bgUpdateGotErrorRetryInMinutes": {
|
"skipUpdateNotifications": "Ne prikazujte obavještenja ažuriranja",
|
||||||
"one": "Provjera ažuriranja u pozadini naišla je na {}, zakazuje se ponovni pokušaj za {} minutu",
|
"updatesAvailableNotifChannel": "Dostupna ažuriranja",
|
||||||
"other": "Provjera ažuriranja u pozadini naišla je na {}, zakazuje se ponovni pokušaj za {} min."
|
"appsUpdatedNotifChannel": "Aplikacije su ažurirane",
|
||||||
},
|
"appsPossiblyUpdatedNotifChannel": "Pokušano ažuriranje aplikacija",
|
||||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
"errorCheckingUpdatesNotifChannel": "Greška pri provjeri ažuriranja",
|
||||||
"one": "Provjera ažuriranja u pozadini je pronašla {} ažuriranje - korisnik će biti obavješten ako je to potrebno",
|
"appsRemovedNotifChannel": "Aplikacije su uklonjene",
|
||||||
"other": "Provjera ažuriranja u pozadini je pronašla {} ažuriranja - korisnik će biti obavješten ako je to potrebno"
|
"downloadingXNotifChannel": "Preuzimanje {}",
|
||||||
},
|
"completeAppInstallationNotifChannel": "Dovršite instalaciju aplikacije",
|
||||||
"apps": {
|
"checkingForUpdatesNotifChannel": "Tražim moguće nadogradnje",
|
||||||
"one": "{} aplikacija",
|
"onlyCheckInstalledOrTrackOnlyApps": "Isključivo provjerite ažuriranje za instalirane i aplikacije 'samo za praćenje'",
|
||||||
"other": "{} aplikacije"
|
"supportFixedAPKURL": "Podržite fiksne APK URL-ove",
|
||||||
},
|
"selectX": "Izaberite {}",
|
||||||
"url": {
|
"parallelDownloads": "Dozvoli paralelna preuzimanja",
|
||||||
"one": "{} URL",
|
"installMethod": "Način instalacije",
|
||||||
"other": "{} URL-ovi"
|
"normal": "normalno",
|
||||||
},
|
"root": "korijen",
|
||||||
"minute": {
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"one": "{} minuta",
|
"useSystemFont": "Koristite sistemski font",
|
||||||
"other": "min."
|
"systemFontError": "Greška pri učitavanju sistemskog fonta: {}",
|
||||||
},
|
"useVersionCodeAsOSVersion": "Koristite kod verzije aplikacije kao verziju koju je otkrio OS",
|
||||||
"hour": {
|
"requestHeader": "Zaglavlje zahtjeva",
|
||||||
"one": "{} sat",
|
"useLatestAssetDateAsReleaseDate": "Koristite najnovije otpremanje materijala kao datum izdavanja",
|
||||||
"other": "{} sat/i"
|
"defaultPseudoVersioningMethod": "Zadana metoda pseudo-verzija",
|
||||||
},
|
"partialAPKHash": "Djelomični APK Hash",
|
||||||
"day": {
|
"APKLinkHash": "APK Link Hash",
|
||||||
"one": "{} dan",
|
"directAPKLink": "Direktna APK veza",
|
||||||
"other": "{} dana"
|
"pseudoVersionInUse": "Pseudo-verzija je u upotrebi",
|
||||||
},
|
"installed": "Instalirano",
|
||||||
"clearedNLogsBeforeXAfterY": {
|
"latest": "Najnoviji",
|
||||||
"one": "Izbrisan {n} log (prije = {before}, nakon = {after})",
|
"invertRegEx": "Obrni regularni izraz",
|
||||||
"other": "Izbrisano {n} log-ova (prije = {before}, nakon = {after})"
|
"note": "Note",
|
||||||
},
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"xAndNMoreUpdatesAvailable": {
|
"removeAppQuestion": {
|
||||||
"one": "{} i još 1 aplikacija ima ažuriranja.",
|
"one": "Želite li ukloniti aplikaciju?",
|
||||||
"other": "{} i još {} aplikacija imaju ažuriranja."
|
"other": "Želite li ukloniti aplikacije?"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesInstalled": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "{} i još 1 aplikacija je ažurirana.",
|
"one": "Previše zahtjeva (ograničena broj zahteva) - pokušajte ponovo za {} minutu",
|
||||||
"other": "{} i još {} aplikacija je ažurirano."
|
"other": "Previše zahtjeva (ograničena cijena) - pokušajte ponovo za {} min."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"bgUpdateGotErrorRetryInMinutes": {
|
||||||
"one": "{} and 1 more app may have been updated.",
|
"one": "Provjera ažuriranja u pozadini naišla je na {}, zakazuje se ponovni pokušaj za {} minutu",
|
||||||
"other": "{} and {} more apps may have been updated."
|
"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": "{} i još jedna aplikacija je vjerovatno ažurirana.",
|
||||||
|
"other": "{} i još {} aplikacija su vjerovatno ažurirane."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,36 +9,36 @@
|
|||||||
"placeholder": "Zástupce",
|
"placeholder": "Zástupce",
|
||||||
"someErrors": "Vyskytly se nějaké chyby",
|
"someErrors": "Vyskytly se nějaké chyby",
|
||||||
"unexpectedError": "Neočekávaná chyba",
|
"unexpectedError": "Neočekávaná chyba",
|
||||||
"ok": "Okay",
|
"ok": "Ok",
|
||||||
"and": "a",
|
"and": "a",
|
||||||
"githubPATLabel": "GitHub Personal Access Token (Raises Rate Limit)",
|
"githubPATLabel": "GitHub Personal Access Token (zvyšuje limit rychlosti)",
|
||||||
"includePrereleases": "includepreleases",
|
"includePrereleases": "Zahrnout předběžné verze",
|
||||||
"fallbackToOlderReleases": "Fallback to older releases",
|
"fallbackToOlderReleases": "Přechod na starší verze",
|
||||||
"filterReleaseTitlesByRegEx": "Názvy vydání podle regulárního výrazu\filtr",
|
"filterReleaseTitlesByRegEx": "Filtrovat názvy verzí podle regulárního výrazu",
|
||||||
"invalidRegEx": "Neplatný regulární výraz",
|
"invalidRegEx": "Neplatný regulární výraz",
|
||||||
"noDescription": "Žádný popis",
|
"noDescription": "Žádný popis",
|
||||||
"cancel": "Zrušit",
|
"cancel": "Zrušit",
|
||||||
"continue": "Pokračovat",
|
"continue": "Pokračovat",
|
||||||
"requiredInBracets": "(Required)",
|
"requiredInBrackets": "(Požadované)",
|
||||||
"dropdownNoOptsError": "ERROR: DROPDOWN MUSÍ MÍT AŽ JEDNU MOŽNOST",
|
"dropdownNoOptsError": "ERROR: DROPDOWN MUSÍ MÍT AŽ JEDNU MOŽNOST",
|
||||||
"color": "barva",
|
"colour": "Barva",
|
||||||
"githubStarredRepos": "GitHub Starred Repos",
|
"githubStarredRepos": "GitHub označená hvězdičkou",
|
||||||
"uname": "username",
|
"uname": "Uživatelské jméno",
|
||||||
"wrongArgNum": "Špatný počet předložených argumentů",
|
"wrongArgNum": "Nesprávný počet zadaných argumentů",
|
||||||
"xIsTrackOnly":"{} je určeno pouze pro sledování",
|
"xIsTrackOnly": "{} je určeno pouze pro sledování",
|
||||||
"source": "zdroj",
|
"source": "Zdroj",
|
||||||
"app": "App",
|
"app": "Aplikace",
|
||||||
"appsFromSourceAreTrackOnly": "Aplikace z tohoto zdroje jsou 'Jen sledovány'.",
|
"appsFromSourceAreTrackOnly": "Aplikace z tohoto zdroje jsou Jen sledovány.",
|
||||||
"youPickedTrackOnly": "Vybrali jste možnost 'Jen sledovat'.",
|
"youPickedTrackOnly": "Vybrali jste možnost Jen sledovat.",
|
||||||
"trackOnlyAppDescription": "Aplikace je sledována kvůli aktualizacím, ale Obtainium ji nebude stahovat ani instalovat.",
|
"trackOnlyAppDescription": "Aplikace je sledována kvůli aktualizacím, ale Obtainium ji nebude stahovat ani instalovat.",
|
||||||
"cancelled": "Zrušeno",
|
"cancelled": "Zrušeno",
|
||||||
"appAlreadyAdded": "Aplikace již přidána",
|
"appAlreadyAdded": "Aplikace již přidána",
|
||||||
"alreadyUpToDateQuestion": "App already up to date?",
|
"alreadyUpToDateQuestion": "App already up to date?",
|
||||||
"addApp": "Přidat aplikaci",
|
"addApp": "Přidat aplikaci",
|
||||||
"appSourceURL": "zdrojová adresa URL aplikace",
|
"appSourceURL": "Zdrojová adresa URL aplikace",
|
||||||
"error": "Chyba",
|
"error": "Chyba",
|
||||||
"add": "Přidat",
|
"add": "Přidat",
|
||||||
"searchSomeSourcesLabel": "Vyhledávání (pouze konkrétní zdroje)",
|
"searchSomeSourcesLabel": "Vyhledávání (pouze pro určité zdroje)",
|
||||||
"search": "Hledat",
|
"search": "Hledat",
|
||||||
"additionalOptsFor": "Další možnosti pro {}",
|
"additionalOptsFor": "Další možnosti pro {}",
|
||||||
"supportedSources": "Podporované zdroje",
|
"supportedSources": "Podporované zdroje",
|
||||||
@@ -46,223 +46,260 @@
|
|||||||
"searchableInBrackets": "(s možností vyhledávání)",
|
"searchableInBrackets": "(s možností vyhledávání)",
|
||||||
"appsString": "Apky",
|
"appsString": "Apky",
|
||||||
"noApps": "Žádné aplikace",
|
"noApps": "Žádné aplikace",
|
||||||
"noAppsForFilter": "žádné aplikace pro vybraný filtr",
|
"noAppsForFilter": "Žádné aplikace pro vybraný filtr",
|
||||||
"byX": "By {}",
|
"byX": "Od {}",
|
||||||
"percentProgress": "Pokrok: {}%",
|
"percentProgress": "Pokrok: {}%",
|
||||||
"pleaseWait": "Počkejte prosím",
|
"pleaseWait": "Počkejte prosím",
|
||||||
"updateAvailable": "Aktualizace je k dispozici",
|
"updateAvailable": "Aktualizace je k dispozici",
|
||||||
"estimateInBracketsShort": "(approx.)",
|
|
||||||
"notInstalled": "Není nainstalováno",
|
"notInstalled": "Není nainstalováno",
|
||||||
"estimateInBrackets": "(přibližně)",
|
"pseudoVersion": "pseudoverze",
|
||||||
"selectAll": "Vybrat Vše",
|
"selectAll": "Vybrat vše",
|
||||||
"deselectN": "{} deselected",
|
"deselectX": "{} deselected",
|
||||||
"xWillBeRemovedButRemainInstalled":"{} bude odstraněn z Obtainium, ale zůstane nainstalován v zařízení.",
|
"xWillBeRemovedButRemainInstalled": "{} bude odstraněn z Obtainium, ale zůstane nainstalován v zařízení.",
|
||||||
"removeSelectedAppsQuestion": "Odebrat vybrané aplikace?",
|
"removeSelectedAppsQuestion": "Odebrat vybrané aplikace?",
|
||||||
"removeSelectedApps": "Odebrat vybrané aplikace",
|
"removeSelectedApps": "Odebrat vybrané aplikace",
|
||||||
"updateX": "Aktualizovat {}",
|
"updateX": "Aktualizovat {}",
|
||||||
"installX": "Instalovat {}",
|
"installX": "Instalovat {}",
|
||||||
"markXTrackOnlyAsUpdated": "Označit {}\n(Track-Only)\njako aktualizované",
|
"markXTrackOnlyAsUpdated": "Označit {}\n(Jen sledované)\njako aktualizované",
|
||||||
"changeX": "Změnit {}",
|
"changeX": "Změnit {}",
|
||||||
"installUpdateApps": "Instalovat/aktualizovat aplikace",
|
"installUpdateApps": "Instalovat/aktualizovat aplikace",
|
||||||
"installUpdateSelectedApps": "Instalovat/aktualizovat vybrané aplikace",
|
"installUpdateSelectedApps": "Instalovat/aktualizovat vybrané aplikace",
|
||||||
"markXSelectedAppsAsUpdated": "označit {} vybrané aplikace jako aktuální?",
|
"markXSelectedAppsAsUpdated": "Označit {} vybrané aplikace jako aktuální?",
|
||||||
"no": "Ne",
|
"no": "Ne",
|
||||||
"yes": "ano",
|
"yes": "Ano",
|
||||||
"markSelectedAppsUpdated": "označit vybrané aplikace jako aktuální",
|
"markSelectedAppsUpdated": "Označit vybrané aplikace jako aktuální",
|
||||||
"pinToTop": "Připnout nahoru",
|
"pinToTop": "Připnout nahoru",
|
||||||
"unpinFromTop": "'Unpin Top'",
|
"unpinFromTop": "Odepnout shora",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "Obnovit stav instalace vybraných aplikací?",
|
"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ů.",
|
"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ů.",
|
||||||
|
"customLinkMessage": "Tyto odkazy fungují na zařízeních s nainstalovaným Obtainium",
|
||||||
|
"shareAppConfigLinks": "Sdílet konfiguraci aplikace jako odkaz HTML",
|
||||||
"shareSelectedAppURLs": "Sdílet adresy URL vybraných aplikací",
|
"shareSelectedAppURLs": "Sdílet adresy URL vybraných aplikací",
|
||||||
"resetInstallStatus": "Obnovení stavu instalace",
|
"resetInstallStatus": "Obnovit stav instalace",
|
||||||
"more": "more",
|
"more": "Více",
|
||||||
"removeOutdatedFilter": "Odstranit filtr aplikace 'Not Current'",
|
"removeOutdatedFilter": "Odstranit filtr Neaktuální",
|
||||||
"showOutdatedOnly": "Zobrazit pouze aplikace, které nejsou aktuální",
|
"showOutdatedOnly": "Zobrazovat pouze zastaralé aplikace",
|
||||||
"filter": "Filtr",
|
"filter": "Filtr",
|
||||||
"filterActive": "Filtr *",
|
"filterActive": "Filtr *",
|
||||||
"filterApps": "Filtrovat aplikace",
|
"filterApps": "Filtrovat aplikace",
|
||||||
"appName": "název aplikace",
|
"appName": "Název aplikace",
|
||||||
"author": "Autor",
|
"author": "Autor",
|
||||||
"upToDateApps": "Apps with current version",
|
"upToDateApps": "Aktuální apky",
|
||||||
"nonInstalledApps": "Apps not installed",
|
"nonInstalledApps": "Neinstalované apky",
|
||||||
"importExport": "Import/Export",
|
"importExport": "Import Export",
|
||||||
"settings": "Nastavení",
|
"settings": "Nastavení",
|
||||||
"exportedTo": "Exportováno do {}",
|
"exportedTo": "Exportováno do {}",
|
||||||
"obtainiumExport": "Obtainium Export",
|
"obtainiumExport": "Export obtainia",
|
||||||
"invalidInput": "Neplatný vstup",
|
"invalidInput": "Neplatný vstup",
|
||||||
"importedX": "Importováno {}",
|
"importedX": "Importováno {}",
|
||||||
"obtainiumImport": "Obtainium Import",
|
"obtainiumImport": "Dovoz obtainia",
|
||||||
"importFromURLList": "Import ze seznamu URL",
|
"importFromURLList": "Import ze seznamu URL",
|
||||||
"searchQuery": "Search Query",
|
"searchQuery": "Vyhledávací dotaz",
|
||||||
"appURLList": "App URL List",
|
"appURLList": "Seznam adres aplikací",
|
||||||
"line": "line",
|
"line": "Linka",
|
||||||
"searchX": "Search {}",
|
"searchX": "Vyhledávání {}",
|
||||||
"noResults": "Nebyly nalezeny žádné výsledky",
|
"noResults": "Nebyly nalezeny žádné výsledky",
|
||||||
"importX": "Import {}",
|
"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.",
|
"importedAppsIdDisclaimer": "Importované aplikace mohou být nesprávně zobrazeny jako \"Neinstalovány\". 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",
|
"importErrors": "Chyba importu",
|
||||||
"importedXOfYApps":"{}importováno {}aplikací.",
|
"importedXOfYApps": "{}importováno z {} aplikací.",
|
||||||
"followingURLsHadErrors": "U následujících adres URL došlo k chybám:",
|
"followingURLsHadErrors": "U následujících adres došlo k chybám:",
|
||||||
"okay": "Okay",
|
"selectURL": "Vybrat adresu",
|
||||||
"selectURL": "Select URL",
|
"selectURLs": "Select adresy",
|
||||||
"selectURLs": "Select URLs",
|
|
||||||
"pick": "Vybrat",
|
"pick": "Vybrat",
|
||||||
"theme": "Téma",
|
"theme": "Téma",
|
||||||
"dark": "Tmavé",
|
"dark": "Tmavé",
|
||||||
"light": "Světlé",
|
"light": "Světlé",
|
||||||
"followSystem": "Follow System",
|
"followSystem": "Jako systém",
|
||||||
"obtainium": "Obtainium",
|
|
||||||
"materialYou": "Material You",
|
|
||||||
"useBlackTheme": "Použít čistě černé tmavé téma",
|
"useBlackTheme": "Použít čistě černé tmavé téma",
|
||||||
"appSortBy": "Seřadit aplikaci podle",
|
"appSortBy": "Seřadit podle",
|
||||||
"authorName": "autor/jméno",
|
"authorName": "Autor/Jméno",
|
||||||
"nameAuthor": "jméno/autor",
|
"nameAuthor": "Jméno/Autor",
|
||||||
"asAdded": "AsAdded",
|
"asAdded": "Přidáno",
|
||||||
"appSortOrder": "Sort App By",
|
"appSortOrder": "Seřadit",
|
||||||
"ascending": "Vzestupně",
|
"ascending": "Vzestupně",
|
||||||
"descending": "Sestupně",
|
"descending": "Sestupně",
|
||||||
"bgUpdateCheckInterval": "Background Update Check Interval",
|
"bgUpdateCheckInterval": "Interval kontroly aktualizace na pozadí",
|
||||||
"neverManualOnly": "Nikdy - pouze ručně",
|
"neverManualOnly": "Nikdy - pouze ručně",
|
||||||
"appearance": "Vzhled",
|
"appearance": "Vzhled",
|
||||||
"showWebInAppView": "Zobrazit zdrojové webové stránky v zobrazení aplikace",
|
"showWebInAppView": "Zobrazit zdrojové webové stránky v zobrazení aplikace",
|
||||||
"pinUpdates": "Připnout aplikace s aktualizacemi nahoře",
|
"pinUpdates": "Připnout aplikace s aktualizacemi nahoru",
|
||||||
"updates": "Updates",
|
"updates": "Aktualizace",
|
||||||
"sourceSpecific": "source specific",
|
"sourceSpecific": "Specifické pro zdroj",
|
||||||
"appSource": "zdroj aplikace",
|
"appSource": "Zdroj aplikace",
|
||||||
"noLogs": "Žádné protokoly",
|
"noLogs": "Žádné protokoly",
|
||||||
"appLogs": "App Logs",
|
"appLogs": "Záznamy apky",
|
||||||
"close": "Zavřít",
|
"close": "Zavřít",
|
||||||
"share": "Sdílet",
|
"share": "Sdílet",
|
||||||
"appNotFound": "App not found",
|
"appNotFound": "Aplikace nenalezena",
|
||||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
||||||
"pickAnAPK": "Vybrat APK",
|
"pickAnAPK": "Vybrat APK",
|
||||||
"appHasMoreThanOnePackage":"{} má více než jeden balíček:",
|
"appHasMoreThanOnePackage": "{} má více než jeden balíček:",
|
||||||
"deviceSupportsXArch": "Vaše zařízení podporuje architekturu CPU {}.",
|
"deviceSupportsXArch": "Vaše zařízení podporuje architekturu CPU {}.",
|
||||||
"deviceSupportsFollowingArchs": "Vaše zařízení podporuje následující architektury CPU:",
|
"deviceSupportsFollowingArchs": "Vaše zařízení podporuje následující architektury CPU:",
|
||||||
"warning": "Varování",
|
"warning": "Varování",
|
||||||
"sourceIsXButPackageFromYPrompt": "The app source is '{}' but the release package is from '{}'. Pokračovat?",
|
"sourceIsXButPackageFromYPrompt": "Zdroj aplikace je '{}', ale balíček pro vydání je z '{}'. Pokračovat?",
|
||||||
"updatesAvailable": "dostupné aktualizace",
|
"updatesAvailable": "Dostupné aktualizace",
|
||||||
"updatesAvailableNotifDescription": "Upozorňuje uživatele, že jsou k dispozici aktualizace pro jednu nebo více aplikací sledovaných Obtainium",
|
"updatesAvailableNotifDescription": "Upozorňuje uživatele, že jsou k dispozici aktualizace pro jednu nebo více aplikací sledovaných Obtainium",
|
||||||
"noNewUpdates": "Žádné nové aktualizace.",
|
"noNewUpdates": "Žádné nové aktualizace.",
|
||||||
"xHasAnUpdate":"{} má aktualizaci.",
|
"xHasAnUpdate": "{} má aktualizaci.",
|
||||||
"appsUpdated": "Aplikace aktualizovány",
|
"appsUpdated": "Aplikace aktualizovány",
|
||||||
"appsUpdatedNotifDescription": "Upozorňuje uživatele, že byly provedeny aktualizace jedné nebo více aplikací na pozadí",
|
"appsUpdatedNotifDescription": "Upozornit, že byly provedeny aktualizace jedné nebo více aplikací na pozadí",
|
||||||
"xWasUpdatedToY":"{} byl aktualizován na {}",
|
"xWasUpdatedToY": "{} byla aktualizována na {}",
|
||||||
"errorCheckingUpdates": "Chybová kontrola aktualizací",
|
"errorCheckingUpdates": "Chyba kontroly aktualizací",
|
||||||
"errorCheckingUpdatesNotifDescription": "Oznámení zobrazené při neúspěšné kontrole aktualizací na pozadí",
|
"errorCheckingUpdatesNotifDescription": "Zobrazit oznámení při neúspěšné kontrole aktualizací na pozadí",
|
||||||
"appsRemoved": "Odstraněné aplikace",
|
"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í",
|
"appsRemovedNotifDescription": "Oznámit, že jedna nebo více aplikací bylo odstraněno z důvodu chyb při načítání",
|
||||||
"xWasRemovedDueToErrorY":"{} byla odstraněna z důvodu následující chyby: {}",
|
"xWasRemovedDueToErrorY": "{} byla odstraněna z důvodu následující chyby: {}",
|
||||||
"completeAppInstallation": "Dokončit instalaci aplikace",
|
"completeAppInstallation": "Dokončit instalaci aplikace",
|
||||||
"obtainiumMustBeOpenToInstallApps": "Obtainium musí být otevřeno, aby bylo možné instalovat 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í",
|
"completeAppInstallationNotifDescription": "Vyzvat k návratu do Obtainium pro dokončení instalace aplikací",
|
||||||
"checkingForUpdates": "Zkontrolovat aktualizace",
|
"checkingForUpdates": "Zkontrolovat aktualizace",
|
||||||
"checkingForUpdatesNotifDescription": "Dočasné oznámení zobrazené při kontrole aktualizací",
|
"checkingForUpdatesNotifDescription": "Dočasné oznámení zobrazené při kontrole aktualizací",
|
||||||
"pleaseAllowInstallPerm": "Povolte prosím Obtainium instalovat aplikace",
|
"pleaseAllowInstallPerm": "Povolte prosím Obtainium instalovat aplikace",
|
||||||
"trackOnly": "Jen sledovat",
|
"trackOnly": "Jen sledovat",
|
||||||
"errorWithHttpStatusCode": "error {}",
|
"errorWithHttpStatusCode": "Chyba {}",
|
||||||
"versionCorrectionDisabled": "Oprava verze zakázána (zásuvný modul zřejmě nefunguje)",
|
"versionCorrectionDisabled": "Oprava verze zakázána (zásuvný modul zřejmě nefunguje)",
|
||||||
"unknown": "Unknown",
|
"unknown": "Neznám",
|
||||||
"none": "None",
|
"none": "Žádný",
|
||||||
"never": "Nikdy",
|
"never": "Nikdy",
|
||||||
"latestVersionX": "Nejnovější verze: {}",
|
"latestVersionX": "Nejnovější verze: {}",
|
||||||
"installedVersionX": "Nainstalovaná verze: {}",
|
"installedVersionX": "Nainstalovaná verze: {}",
|
||||||
"lastUpdateCheckX": "Poslední kontrola aktualizace: {}",
|
"lastUpdateCheckX": "Poslední kontrola aktualizace: {}",
|
||||||
"remove": "Odebrat",
|
"remove": "Odebrat",
|
||||||
"yesMarkUpdated": "Ano, označit jako aktualizované",
|
"yesMarkUpdated": "Ano, označit jako aktualizované",
|
||||||
"fdroid": "F-Droid Official",
|
"fdroid": "Oficiální repozitář F-Droid",
|
||||||
"appIdOrName": "App ID or Name",
|
"appIdOrName": "ID nebo název apky",
|
||||||
"appId": "App ID",
|
"appId": "ID aplikace",
|
||||||
"appWithIdOrNameNotFound": "Žádná aplikace s tímto ID nebo názvem nebyla nalezena",
|
"appWithIdOrNameNotFound": "Žádná aplikace s tímto ID nebo názvem nebyla nalezena",
|
||||||
"reposHaveMultipleApps": "Repozitáře mohou obsahovat více aplikací",
|
"reposHaveMultipleApps": "Repozitáře mohou obsahovat více aplikací",
|
||||||
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
|
"fdroidThirdPartyRepo": "F-Droid repozitář třetí strany",
|
||||||
"steam": "Steam",
|
|
||||||
"steamMobile": "Steam Mobile",
|
"steamMobile": "Steam Mobile",
|
||||||
"steamChat": "Steam Chat",
|
"steamChat": "Steam Chat",
|
||||||
"install": "Install",
|
"install": "Nainstalujte",
|
||||||
"markInstalled": "Označit jako nainstalovaný",
|
"markInstalled": "Označit jako nainstalovaný",
|
||||||
"update": "Aktualizovat",
|
"update": "Aktualizovat",
|
||||||
"markUpdated": "Označit jako aktuální",
|
"markUpdated": "Označit jako aktuální",
|
||||||
"additionalOptions": "Additional Options",
|
"additionalOptions": "Další možnosti",
|
||||||
"disableVersionDetection": "Zakázat detekci verze",
|
"disableVersionDetection": "Deaktivovat detekci verze",
|
||||||
"noVersionDetectionExplanation": "Tato volba by měla být použita pouze u aplikací, kde detekce verzí nefunguje správně.",
|
"noVersionDetectionExplanation": "Tato možnost by měla být použita pouze u aplikace, kde detekce verzí nefunguje správně.",
|
||||||
"downloadingX": "download {}",
|
"downloadingX": "Stáhnout {}",
|
||||||
"downloadNotifDescription": "Informuje uživatele o průběhu stahování aplikace",
|
"downloadNotifDescription": "Informuje uživatele o průběhu stahování aplikace",
|
||||||
"noAPKFound": "Žádná APK nebyla nalezena",
|
"noAPKFound": "Žádná APK nebyla nalezena",
|
||||||
"noVersionDetection": "Žádná detekce verze",
|
"noVersionDetection": "Žádná detekce verze",
|
||||||
"categorize": "Kategorizovat",
|
"categorize": "Kategorizovat",
|
||||||
"categories": "Kategorie",
|
"categories": "Kategorie",
|
||||||
"category": "kategorie",
|
"category": "Kategorie",
|
||||||
"noCategory": "Žádná kategorie",
|
"noCategory": "Žádná kategorie",
|
||||||
"noCategories": "Žádné kategorie",
|
"noCategories": "Žádné kategorie",
|
||||||
"deleteCategoriesQuestion": "Smazat kategorie?",
|
"deleteCategoriesQuestion": "Smazat kategorie?",
|
||||||
"categoryDeleteWarning": "Všechny aplikace v odstraněných kategoriích budou nastaveny na nekategorizované.",
|
"categoryDeleteWarning": "Všechny aplikace v odstraněných kategoriích budou nastaveny na nekategorizované.",
|
||||||
"addCategory": "přidat kategorii",
|
"addCategory": "Přidat kategorii",
|
||||||
"label": "štítek",
|
"label": "Štítek",
|
||||||
"language": "Jazyk",
|
"language": "Jazyk",
|
||||||
"copiedToClipboard": "zkopírováno do schránky",
|
"copiedToClipboard": "Zkopírováno do schránky",
|
||||||
"storagePermissionDenied": "povolení k ukládání odepřeno",
|
"storagePermissionDenied": "Oprávnění k ukládání odepřeno",
|
||||||
"selectedCategorizeWarning": "Toto nahradí všechna stávající nastavení kategorií pro vybrané aplikace.",
|
"selectedCategorizeWarning": "Toto nahradí všechna stávající nastavení kategorií pro vybrané aplikace.",
|
||||||
"filterAPKsByRegEx": "Filtrovat APK podle regulárního výrazu",
|
"filterAPKsByRegEx": "Filtrovat APK podle regulárního výrazu",
|
||||||
"removeFromObtainium": "Odebrat z Obtainium",
|
"removeFromObtainium": "Odebrat z Obtainium",
|
||||||
"uninstallFromDevice": "Odinstalovat ze zařízení",
|
"uninstallFromDevice": "Odinstalovat ze zařízení",
|
||||||
"onlyWorksWithNonVersionDetectApps": "Funguje pouze pro aplikace s vypnutou detekcí verze.",
|
"onlyWorksWithNonVersionDetectApps": "Funguje pouze pro aplikace s vypnutou detekcí verze.",
|
||||||
"releaseDateAsVersion": "Použít datum vydání jako verzi",
|
"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í.",
|
"releaseDateAsVersionExplanation": "Tato možnost by měla být použita pouze u aplikace, kde detekce verzí nefunguje správně, ale je k dispozici datum vydání.",
|
||||||
"changes": "Změny",
|
"changes": "Změny",
|
||||||
"releaseDate": "datum vydání",
|
"releaseDate": "Datum vydání",
|
||||||
"importFromURLsInFile": "Importovat adresy URL ze souboru (např. OPML)",
|
"importFromURLsInFile": "Importovat adresy URL ze souboru (např. OPML)",
|
||||||
"versionDetection": "detekce verze",
|
"versionDetectionExplanation": "Sloučit řetězec verze s verzí zjištěnou z OS",
|
||||||
"standardVersionDetection": "standardní detekce verze",
|
"versionDetection": "Detekce verze",
|
||||||
|
"standardVersionDetection": "Standardní detekce verze",
|
||||||
"groupByCategory": "Seskupit podle kategorie",
|
"groupByCategory": "Seskupit podle kategorie",
|
||||||
"autoApkFilterByArch": "Pokud je to možné, pokuste se filtrovat soubory APK podle architektury procesoru",
|
"autoApkFilterByArch": "Pokud je to možné, pokuste se filtrovat soubory APK podle architektury procesoru",
|
||||||
"overrideSource": "Přepsat zdroj",
|
"overrideSource": "Přepsat zdroj",
|
||||||
"dontShowAgain": "Nezobrazovat znovu",
|
"dontShowAgain": "Nezobrazovat znovu",
|
||||||
"dontShowTrackOnlyWarnings": "Nezobrazovat varování pro 'Track Only'",
|
"dontShowTrackOnlyWarnings": "Nezobrazovat varování pro 'Jen sledované'",
|
||||||
"dontShowAPKOriginWarnings": "Nezobrazovat varování pro původ APK",
|
"dontShowAPKOriginWarnings": "Nezobrazovat varování pro původ APK",
|
||||||
"moveNonInstalledAppsToBottom": "Přesunout nenainstalované aplikace na konec zobrazení Aplikace",
|
"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)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(Umožňuje vyhledávání a lepší zjišťování APK)",
|
||||||
"about": "About",
|
"about": "O",
|
||||||
"requiresCredentialsInSettings": "Vyžaduje další pověření (v nastavení)",
|
"requiresCredentialsInSettings": "{}: Vyžaduje další pověření (v nastavení)",
|
||||||
"checkOnStart": "Zkontrolovat jednou při spuštění",
|
"checkOnStart": "Zkontrolovat jednou při spuštění",
|
||||||
"tryInferAppIdFromCode": "Pokusit se určit ID aplikace ze zdrojového kódu",
|
"tryInferAppIdFromCode": "Pokusit se určit ID aplikace ze zdrojového kódu",
|
||||||
"removeOnExternalUninstall": "Automaticky odstranit externě odinstalované aplikace",
|
"removeOnExternalUninstall": "Automaticky odstranit externě odinstalované aplikace",
|
||||||
"pickHighestVersionCode": "Automaticky vybrat APK s kódem nejvyšší verze",
|
"pickHighestVersionCode": "Automaticky vybrat nejvyšší verzi APK",
|
||||||
"checkUpdateOnDetailPage": "Zkontrolovat aktualizace při otevření stránky s podrobnostmi aplikace",
|
"checkUpdateOnDetailPage": "Zkontrolovat aktualizaci při otevření stránky s podrobnostmi aplikace",
|
||||||
"disablePageTransitions": "Zakázat animace pro přechody stránek",
|
"disablePageTransitions": "Zakázat animace pro přechody stránek",
|
||||||
"reversePageTransitions": "Obrátit animace pro přechody stránek",
|
"reversePageTransitions": "Obrátit animace pro přechody stránek",
|
||||||
"minStarCount": "Minimální počet hvězdiček",
|
"minStarCount": "Minimální počet hvězdiček",
|
||||||
"addInfoBelow": "Přidat tuto informaci na konec stránky",
|
"addInfoBelow": "Přidat tuto informaci na konec stránky.",
|
||||||
"addInfoInSettings": "Přidat tuto informaci do nastavení.",
|
"addInfoInSettings": "Přidat tuto informaci do nastavení.",
|
||||||
"githubSourceNote": "Omezení rychlosti GitHub lze obejít pomocí klíče API.",
|
"githubSourceNote": "Omezení rychlosti GitHub lze obejít pomocí klíče API.",
|
||||||
"gitlabSourceNote": "Extrakce GitLab APK nemusí fungovat bez klíče API",
|
"gitlabSourceNote": "Extrakce GitLab APK nemusí fungovat bez klíče API",
|
||||||
"sortByFileNamesNotLinks": "Řadit podle názvů souborů místo celých odkazů",
|
"sortByLastLinkSegment": "Seřadit pouze podle poslední části odkazu",
|
||||||
"filterReleaseNotesByRegEx": "Filtrovat poznámky k vydání podle regulárního výrazu",
|
"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$')",
|
"customLinkFilterRegex": "Vlastní filtr odkazů APK podle regulárního výrazu (výchozí '.apk$')",
|
||||||
"appsPossiblyUpdated": "Byly provedeny pokusy o aktualizaci aplikací",
|
"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í",
|
"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 {}.",
|
"xWasPossiblyUpdatedToY": "{} mohlo být aktualizováno na {}.",
|
||||||
"enableBackgroundUpdates": "Povolit aktualizace na pozadí",
|
"enableBackgroundUpdates": "Povolit aktualizace na pozadí",
|
||||||
"backgroundUpdateReqsExplanation": "Aktualizace na pozadí nemusí být možné pro všechny aplikace.",
|
"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.",
|
"backgroundUpdateLimitsExplanation": "Úspěšnost instalace na pozadí lze určit pouze v případě, že je otevřeno Obtainium.",
|
||||||
"verifyLatestTag": "Ověřit značku 'latest'",
|
"verifyLatestTag": "Zkontrolovat značku latest",
|
||||||
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
|
"intermediateLinkRegex": "Filtrovat mezipropojení, které by mělo být navštíveno jako první",
|
||||||
"intermediateLinkNotFound": "Intermediate link not found",
|
"filterByLinkText": "Filtrovat odkazy podle textu odkazu",
|
||||||
"exemptFromBackgroundUpdates": "Vyloučit aktualizace na pozadí (pokud jsou povoleny)",
|
"intermediateLinkNotFound": "Připojený odkaz nenalezen",
|
||||||
"bgUpdatesOnWiFiOnly": "Zakázat aktualizace na pozadí, pokud není přítomna Wi-Fi",
|
"intermediateLink": "Připojený odkaz",
|
||||||
"autoSelectHighestVersionCode": "Automatický výběr nejvyššího kódu verze APK",
|
"exemptFromBackgroundUpdates": "Vyloučit z aktualizací na pozadí (je-li povoleno)",
|
||||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
"bgUpdatesOnWiFiOnly": "Deaktivovat aktualizace na pozadí, pokud není k dispozici Wi-Fi",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"autoSelectHighestVersionCode": "Automaticky vybrat nejvyšší verzi APK",
|
||||||
|
"versionExtractionRegEx": "Extrakce verze pomocí RegEx",
|
||||||
|
"matchGroupToUse": "Odpovídá použité skupině",
|
||||||
"highlightTouchTargets": "Zvýraznit méně zjevné cíle dotyku",
|
"highlightTouchTargets": "Zvýraznit méně zjevné cíle dotyku",
|
||||||
"pickExportDir": "Vybrat adresář pro export",
|
"pickExportDir": "Vybrat adresář pro export",
|
||||||
"autoExportOnChanges": "Automatický export při změnách",
|
"autoExportOnChanges": "Automatický export při změně",
|
||||||
"filterVersionsByRegEx": "Filtrovat verze podle regulárního výrazu",
|
"includeSettings": "Zahrnout nastavení",
|
||||||
"trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovaný kód verze APK",
|
"filterVersionsByRegEx": "Filtrovat verze podle regulárních výrazů",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovanou verzi APK",
|
||||||
"reverseSort": "Reverse sorting",
|
"dontSortReleasesList": "Seřadit vydání z rozhraní API",
|
||||||
"debugMenu": "Debug Menu",
|
"reverseSort": "Obrácené třídění",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"takeFirstLink": "Použít první odkaz",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"skipSort": "Přeskočit třídění",
|
||||||
|
"debugMenu": "Nabídka ladění",
|
||||||
|
"bgTaskStarted": "Spuštěna úloha na pozadí - zkontrolujte protokoly.",
|
||||||
|
"runBgCheckNow": "Spustit kontrolu aktualizací na pozadí nyní",
|
||||||
|
"versionExtractWholePage": "Použít extrakci verze pomocí RegEx na celou stránku",
|
||||||
|
"installing": "Instaluji",
|
||||||
|
"skipUpdateNotifications": "Neposkytovat oznámení o aktualizaci",
|
||||||
|
"updatesAvailableNotifChannel": "Dostupné aktualizace",
|
||||||
|
"appsUpdatedNotifChannel": "Apky aktualizovány",
|
||||||
|
"appsPossiblyUpdatedNotifChannel": "Byly provedeny pokusy o aktualizace aplikací",
|
||||||
|
"errorCheckingUpdatesNotifChannel": "Chyba při kontrole aktualizací",
|
||||||
|
"appsRemovedNotifChannel": "Odstraněné apky",
|
||||||
|
"downloadingXNotifChannel": "Stáhnout {}",
|
||||||
|
"completeAppInstallationNotifChannel": "Dokončit instalaci aplikace",
|
||||||
|
"checkingForUpdatesNotifChannel": "Zkontrolovat aktualizace",
|
||||||
|
"onlyCheckInstalledOrTrackOnlyApps": "Na aktualizace kontrolovat pouze nainstalované aplikace a aplikace označené Track only",
|
||||||
|
"supportFixedAPKURL": "Odhadnout novější verzi na základě prvních třiceti číslic kontrolního součtu adresy URL APK, pokud není podporována jinak",
|
||||||
|
"selectX": "Vybrat {}",
|
||||||
|
"parallelDownloads": "Povolit souběžné stahování",
|
||||||
|
"installMethod": "Metoda instalace",
|
||||||
|
"normal": "Normální",
|
||||||
|
"root": "Správce",
|
||||||
|
"shizukuBinderNotFound": "Shizuku neběží",
|
||||||
|
"useSystemFont": "Použít systémové písmo",
|
||||||
|
"systemFontError": "Chyba při načítání systémového písma: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Použít kód verze aplikace jako verzi zjištěnou OS",
|
||||||
|
"requestHeader": "Hlavička požadavku",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Použít poslední nahrané dílo jako datum vydání",
|
||||||
|
"defaultPseudoVersioningMethod": "Výchozí metoda pseudoverze",
|
||||||
|
"partialAPKHash": "Částečný hash APK",
|
||||||
|
"APKLinkHash": "APK Link Hash",
|
||||||
|
"directAPKLink": "Přímý odkaz APK",
|
||||||
|
"pseudoVersionInUse": "Pseudoverze se používá",
|
||||||
|
"installed": "Instalováno",
|
||||||
|
"latest": "Nejnovější",
|
||||||
|
"invertRegEx": "Invertovat regulární výraz",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Odstranit Apku?",
|
"one": "Odstranit Apku?",
|
||||||
"other": "Odstranit Apky?"
|
"other": "Odstranit Apky?"
|
||||||
@@ -272,48 +309,47 @@
|
|||||||
"other": "Příliš mnoho požadavků (omezená rychlost) - zkuste to znovu za {} minut"
|
"other": "Příliš mnoho požadavků (omezená rychlost) - zkuste to znovu za {} minut"
|
||||||
},
|
},
|
||||||
"bgUpdateGotErrorRetryInMinutes": {
|
"bgUpdateGotErrorRetryInMinutes": {
|
||||||
"one": "Při kontrole aktualizace na pozadí byla zjištěna chyba {}, opakování pokusu bude naplánováno za {} minut",
|
"one": "Při kontrole aktualizace na pozadí byla zjištěna chyba {}, opakování 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"
|
"other": "Při kontrole aktualizací na pozadí byla zjištěna chyba {}, opakování bude naplánováno za {} minut"
|
||||||
},
|
},
|
||||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||||
"one": "Při kontrole aktualizací na pozadí nalezena {}aktualizace - v případě potřeby upozorní uživatele",
|
"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"
|
"other": "Kontrola aktualizací na pozadí nalezla {} aktualizací - v případě potřeby upozorní uživatele"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"one":"{} App",
|
"one": "{} Apka",
|
||||||
"other":"{} apps"
|
"other": "{} Apky"
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"jedna": "{} URL",
|
"one": "{} Adresa",
|
||||||
"other": "{} URLs"
|
"other": "{} Adres"
|
||||||
},
|
},
|
||||||
"minute": {
|
"minute": {
|
||||||
"one":"{} minute",
|
"one": "{} Minuta",
|
||||||
"other": "{} minutes"
|
"other": "{} Minut"
|
||||||
},
|
},
|
||||||
"hour": {
|
"hour": {
|
||||||
"jedna": "{} hodina",
|
"one": "{} Hodina",
|
||||||
"other": "{} hours"
|
"other": "{} Hodin"
|
||||||
},
|
},
|
||||||
"day": {
|
"day": {
|
||||||
"jedna": "{} den",
|
"one": "{} Den",
|
||||||
"other": "{} dny"
|
"other": "{} Dnů"
|
||||||
},
|
},
|
||||||
"clearedNLogsBeforeXAfterY": {
|
"clearedNLogsBeforeXAfterY": {
|
||||||
"one":"{n} log vymazán (před = {před}, po = {po})",
|
"one": "{n} Záznam vymazán (před = {before}, po = {after})",
|
||||||
"other": "{n} logů vymazáno (před = {před}, po = {po})"
|
"other": "{n} Záznamů vymazáno (před = {before}, po = {after})"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesAvailable": {
|
"xAndNMoreUpdatesAvailable": {
|
||||||
"one":"{} a 1 další aplikace mají aktualizace.",
|
"one": "{} a 1 další aplikace mají aktualizace.",
|
||||||
"other":"{} a {} další aplikace mají aktualizace."
|
"other": "{} a {} další aplikace mají aktualizace."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesInstalled": {
|
"xAndNMoreUpdatesInstalled": {
|
||||||
"one":"{} a {} další aplikace mají aktualizace.",
|
"one": "{} a 1 další aplikace mají aktualizace.",
|
||||||
"další":"{} a {} další aplikace byly aktualizovány."
|
"other": "{} a {} další aplikace byly aktualizovány."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
"one":"{} a {} další aplikace byly možná aktualizovány",
|
"one": "{} a 1 další aplikace možno aktualizovat",
|
||||||
"other":"{} a {} další aplikace mohly být aktualizovány."
|
"other": "{} a {} další aplikace mohou být aktualizovány."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"colour": "Farbe",
|
"colour": "Farbe",
|
||||||
"githubStarredRepos": "GitHub Starred Repos",
|
"githubStarredRepos": "GitHub Starred Repos",
|
||||||
"uname": "Benutzername",
|
"uname": "Benutzername",
|
||||||
"wrongArgNum": "Falsche Anzahl von Argumenten übermittelt",
|
"wrongArgNum": "Falsche Anzahl von Argumenten (Parametern) übermittelt",
|
||||||
"xIsTrackOnly": "{} ist nur zur Nachverfolgung",
|
"xIsTrackOnly": "{} ist nur zur Nachverfolgung",
|
||||||
"source": "Quelle",
|
"source": "Quelle",
|
||||||
"app": "App",
|
"app": "App",
|
||||||
@@ -51,11 +51,10 @@
|
|||||||
"percentProgress": "Fortschritt: {}%",
|
"percentProgress": "Fortschritt: {}%",
|
||||||
"pleaseWait": "Bitte warten",
|
"pleaseWait": "Bitte warten",
|
||||||
"updateAvailable": "Aktualisierung verfügbar",
|
"updateAvailable": "Aktualisierung verfügbar",
|
||||||
"estimateInBracketsShort": "(ca.)",
|
|
||||||
"notInstalled": "Nicht installiert",
|
"notInstalled": "Nicht installiert",
|
||||||
"estimateInBrackets": "(Ungefähr)",
|
"pseudoVersion": "Pseudoversion",
|
||||||
"selectAll": "Alle auswählen",
|
"selectAll": "Alle auswählen",
|
||||||
"deselectN": "{} abgewählt",
|
"deselectX": "{} abgewählt",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} wird aus Obtainium entfernt, bleibt aber auf dem Gerät installiert.",
|
"xWillBeRemovedButRemainInstalled": "{} wird aus Obtainium entfernt, bleibt aber auf dem Gerät installiert.",
|
||||||
"removeSelectedAppsQuestion": "Ausgewählte Apps entfernen?",
|
"removeSelectedAppsQuestion": "Ausgewählte Apps entfernen?",
|
||||||
"removeSelectedApps": "Ausgewählte Apps entfernen",
|
"removeSelectedApps": "Ausgewählte Apps entfernen",
|
||||||
@@ -73,10 +72,12 @@
|
|||||||
"unpinFromTop": "„Oben anheften“ aufheben",
|
"unpinFromTop": "„Oben anheften“ aufheben",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "Installationsstatus für ausgewählte Apps zurücksetzen?",
|
"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.",
|
"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.",
|
||||||
|
"customLinkMessage": "Diese Links funktionieren auf Geräten, wo Obtainium installiert ist",
|
||||||
|
"shareAppConfigLinks": "Teile die Appkonfiguration als HTML-Link",
|
||||||
"shareSelectedAppURLs": "Ausgewählte App-URLs teilen",
|
"shareSelectedAppURLs": "Ausgewählte App-URLs teilen",
|
||||||
"resetInstallStatus": "Installationsstatus zurücksetzen",
|
"resetInstallStatus": "Installationsstatus zurücksetzen",
|
||||||
"more": "Mehr",
|
"more": "Mehr",
|
||||||
"removeOutdatedFilter": "App-Filter 'Nicht aktuell' entfernen",
|
"removeOutdatedFilter": "App-Filter ‚Nicht aktuell‘ entfernen",
|
||||||
"showOutdatedOnly": "Nur nicht aktuelle Apps anzeigen",
|
"showOutdatedOnly": "Nur nicht aktuelle Apps anzeigen",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"filterActive": "Filter *",
|
"filterActive": "Filter *",
|
||||||
@@ -88,22 +89,21 @@
|
|||||||
"importExport": "Import/Export",
|
"importExport": "Import/Export",
|
||||||
"settings": "Einstellungen",
|
"settings": "Einstellungen",
|
||||||
"exportedTo": "Exportiert zu {}",
|
"exportedTo": "Exportiert zu {}",
|
||||||
"obtainiumExport": "Obtainium Export",
|
"obtainiumExport": "Obtainium-Export",
|
||||||
"invalidInput": "Ungültige Eingabe",
|
"invalidInput": "Ungültige Eingabe",
|
||||||
"importedX": "Importiert {}",
|
"importedX": "Importiert {}",
|
||||||
"obtainiumImport": "Obtainium Import",
|
"obtainiumImport": "Obtainium-Import",
|
||||||
"importFromURLList": "Importieren aus URL-Liste",
|
"importFromURLList": "Importieren aus URL-Liste",
|
||||||
"searchQuery": "Suchanfrage",
|
"searchQuery": "Suchanfrage",
|
||||||
"appURLList": "App URL-Liste",
|
"appURLList": "App URL-Liste",
|
||||||
"line": "Linie",
|
"line": "Linie",
|
||||||
"searchX": "Suche {}",
|
"searchX": "Suche {}",
|
||||||
"noResults": "Keine Ergebnisse gefunden",
|
"noResults": "Keine Ergebnisse gefunden",
|
||||||
"importX": "Import {}",
|
"importX": "Importieren {}",
|
||||||
"importedAppsIdDisclaimer": "Importierte Apps werden möglicherweise fälschlicherweise als \"Nicht installiert\" angezeigt. Um dies zu beheben, installieren Sie sie erneut über Obtainium. Dies hat keine Auswirkungen auf App-Daten. Es betrifft nur URL- und Drittanbieter-Importmethoden.",
|
"importedAppsIdDisclaimer": "Importierte Apps werden möglicherweise fälschlicherweise als \"Nicht installiert\" angezeigt. Um dies zu beheben, installieren Sie sie erneut über Obtainium. Dies hat keine Auswirkungen auf App-Daten. Es betrifft nur URL- und Drittanbieter-Importmethoden.",
|
||||||
"importErrors": "Importfehler",
|
"importErrors": "Importfehler",
|
||||||
"importedXOfYApps": "{} von {} Apps importiert.",
|
"importedXOfYApps": "{} von {} Apps importiert.",
|
||||||
"followingURLsHadErrors": "Bei folgenden URLs traten Fehler auf:",
|
"followingURLsHadErrors": "Bei folgenden URLs traten Fehler auf:",
|
||||||
"okay": "Okay",
|
|
||||||
"selectURL": "URL auswählen",
|
"selectURL": "URL auswählen",
|
||||||
"selectURLs": "URLs auswählen",
|
"selectURLs": "URLs auswählen",
|
||||||
"pick": "Auswählen",
|
"pick": "Auswählen",
|
||||||
@@ -111,8 +111,6 @@
|
|||||||
"dark": "Dunkel",
|
"dark": "Dunkel",
|
||||||
"light": "Hell",
|
"light": "Hell",
|
||||||
"followSystem": "System folgen",
|
"followSystem": "System folgen",
|
||||||
"obtainium": "Obtainium",
|
|
||||||
"materialYou": "Material You",
|
|
||||||
"useBlackTheme": "Verwende Pure Black Dark Theme",
|
"useBlackTheme": "Verwende Pure Black Dark Theme",
|
||||||
"appSortBy": "App sortieren nach",
|
"appSortBy": "App sortieren nach",
|
||||||
"authorName": "Autor/Name",
|
"authorName": "Autor/Name",
|
||||||
@@ -122,19 +120,19 @@
|
|||||||
"ascending": "Aufsteigend",
|
"ascending": "Aufsteigend",
|
||||||
"descending": "Absteigend",
|
"descending": "Absteigend",
|
||||||
"bgUpdateCheckInterval": "Prüfintervall für Hintergrundaktualisierung",
|
"bgUpdateCheckInterval": "Prüfintervall für Hintergrundaktualisierung",
|
||||||
"neverManualOnly": "Nie - nur manuell",
|
"neverManualOnly": "Nie – nur manuell",
|
||||||
"appearance": "Aussehen",
|
"appearance": "Aussehen",
|
||||||
"showWebInAppView": "Quellwebseite in der App-Ansicht anzeigen",
|
"showWebInAppView": "Quellwebseite in der App-Ansicht anzeigen",
|
||||||
"pinUpdates": "Apps mit Aktualisierungen oben anheften",
|
"pinUpdates": "Apps mit Aktualisierungen oben anheften",
|
||||||
"updates": "Aktualisierungen",
|
"updates": "Aktualisierungen",
|
||||||
"sourceSpecific": "Quellenspezifisch",
|
"sourceSpecific": "Quellenspezifisch",
|
||||||
"appSource": "App-Quelle",
|
"appSource": "App-Quelle",
|
||||||
"noLogs": "Keine Protokolle",
|
"noLogs": "Keine Logs",
|
||||||
"appLogs": "App Protokolle",
|
"appLogs": "App-Logs",
|
||||||
"close": "Schließen",
|
"close": "Schließen",
|
||||||
"share": "Teilen",
|
"share": "Teilen",
|
||||||
"appNotFound": "App nicht gefunden",
|
"appNotFound": "App nicht gefunden",
|
||||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
"obtainiumExportHyphenatedLowercase": "Obtainium-Export",
|
||||||
"pickAnAPK": "APK auswählen",
|
"pickAnAPK": "APK auswählen",
|
||||||
"appHasMoreThanOnePackage": "{} verfügt über mehr als ein Paket:",
|
"appHasMoreThanOnePackage": "{} verfügt über mehr als ein Paket:",
|
||||||
"deviceSupportsXArch": "Ihr Gerät unterstützt die CPU-Architektur {}.",
|
"deviceSupportsXArch": "Ihr Gerät unterstützt die CPU-Architektur {}.",
|
||||||
@@ -170,15 +168,14 @@
|
|||||||
"lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}",
|
"lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}",
|
||||||
"remove": "Entfernen",
|
"remove": "Entfernen",
|
||||||
"yesMarkUpdated": "Ja, als aktualisiert markieren",
|
"yesMarkUpdated": "Ja, als aktualisiert markieren",
|
||||||
"fdroid": "F-Droid Official",
|
"fdroid": "offizielles F-Droid-Repo",
|
||||||
"appIdOrName": "App ID oder Name",
|
"appIdOrName": "App ID oder Name",
|
||||||
"appId": "App ID",
|
"appId": "App ID",
|
||||||
"appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden",
|
"appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden",
|
||||||
"reposHaveMultipleApps": "Repos können mehrere Apps enthalten",
|
"reposHaveMultipleApps": "Repos können mehrere Apps enthalten",
|
||||||
"fdroidThirdPartyRepo": "F-Droid Drittparteienrepo",
|
"fdroidThirdPartyRepo": "F-Droid Drittparteienrepo",
|
||||||
"steam": "Steam",
|
|
||||||
"steamMobile": "Steam Mobile",
|
"steamMobile": "Steam Mobile",
|
||||||
"steamChat": "Steam Chat",
|
"steamChat": "Steam-Chat",
|
||||||
"install": "Installieren",
|
"install": "Installieren",
|
||||||
"markInstalled": "Als Installiert markieren",
|
"markInstalled": "Als Installiert markieren",
|
||||||
"update": "Aktualisieren",
|
"update": "Aktualisieren",
|
||||||
@@ -212,6 +209,7 @@
|
|||||||
"changes": "Änderungen",
|
"changes": "Änderungen",
|
||||||
"releaseDate": "Veröffentlichungsdatum",
|
"releaseDate": "Veröffentlichungsdatum",
|
||||||
"importFromURLsInFile": "Importieren von URLs aus Datei (z. B. OPML)",
|
"importFromURLsInFile": "Importieren von URLs aus Datei (z. B. OPML)",
|
||||||
|
"versionDetectionExplanation": "Abgleich der Versionsnummer mit der vom Betriebssystem erkannten Version",
|
||||||
"versionDetection": "Versionserkennung",
|
"versionDetection": "Versionserkennung",
|
||||||
"standardVersionDetection": "Standardversionserkennung",
|
"standardVersionDetection": "Standardversionserkennung",
|
||||||
"groupByCategory": "Nach Kategorie gruppieren",
|
"groupByCategory": "Nach Kategorie gruppieren",
|
||||||
@@ -223,7 +221,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Nicht installierte Apps ans Ende der Apps Ansicht verschieben",
|
"moveNonInstalledAppsToBottom": "Nicht installierte Apps ans Ende der Apps Ansicht verschieben",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Aktiviert Suche und bessere APK Entdeckung)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(Aktiviert Suche und bessere APK Entdeckung)",
|
||||||
"about": "Über",
|
"about": "Über",
|
||||||
"requiresCredentialsInSettings": "Benötigt zusätzliche Anmeldedaten (in den Einstellungen)",
|
"requiresCredentialsInSettings": "{}: Benötigt zusätzliche Anmeldedaten (in den Einstellungen)",
|
||||||
"checkOnStart": "Überprüfe einmalig beim Start",
|
"checkOnStart": "Überprüfe einmalig beim Start",
|
||||||
"tryInferAppIdFromCode": "Versuche, die App-ID aus dem Quellcode zu ermitteln",
|
"tryInferAppIdFromCode": "Versuche, die App-ID aus dem Quellcode zu ermitteln",
|
||||||
"removeOnExternalUninstall": "Automatisches Entfernen von extern deinstallierten Apps",
|
"removeOnExternalUninstall": "Automatisches Entfernen von extern deinstallierten Apps",
|
||||||
@@ -236,53 +234,95 @@
|
|||||||
"addInfoInSettings": "Fügen Sie diese Info in den Einstellungen hinzu.",
|
"addInfoInSettings": "Fügen Sie diese Info in den Einstellungen hinzu.",
|
||||||
"githubSourceNote": "Die GitHub-Ratenbegrenzung kann mit einem API-Schlüssel umgangen werden.",
|
"githubSourceNote": "Die GitHub-Ratenbegrenzung kann mit einem API-Schlüssel umgangen werden.",
|
||||||
"gitlabSourceNote": "GitLab APK-Extraktion funktioniert möglicherweise nicht ohne API-Schlüssel",
|
"gitlabSourceNote": "GitLab APK-Extraktion funktioniert möglicherweise nicht ohne API-Schlüssel",
|
||||||
"sortByFileNamesNotLinks": "Sortiere nach Dateinamen, anstelle von ganzen Links",
|
"sortByLastLinkSegment": "Sortiere nur nach dem letzten Teil des Links",
|
||||||
"filterReleaseNotesByRegEx": "Versionshinweise nach regulärem Ausdruck filtern",
|
"filterReleaseNotesByRegEx": "Versionshinweise nach regulärem Ausdruck filtern",
|
||||||
"customLinkFilterRegex": "Benutzerdefinierter APK Link Filter nach Regulärem Ausdruck (Standard '.apk$')",
|
"customLinkFilterRegex": "Benutzerdefinierter APK Link Filter nach Regulärem Ausdruck (Standard '.apk$')",
|
||||||
"appsPossiblyUpdated": "App Aktualisierungen wurden versucht",
|
"appsPossiblyUpdated": "App Aktualisierungen wurden versucht",
|
||||||
"appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Updates für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden",
|
"appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Updates für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden",
|
||||||
"xWasPossiblyUpdatedToY": "{} wurde möglicherweise aktualisiert auf {}.",
|
"xWasPossiblyUpdatedToY": "{} wurde möglicherweise aktualisiert auf {}.",
|
||||||
|
"enableBackgroundUpdates": "Aktiviere Hintergrundaktualisierungen",
|
||||||
"backgroundUpdateReqsExplanation": "Die Hintergrundaktualisierung ist möglicherweise nicht für alle Apps möglich.",
|
"backgroundUpdateReqsExplanation": "Die Hintergrundaktualisierung ist möglicherweise nicht für alle Apps möglich.",
|
||||||
"backgroundUpdateLimitsExplanation": "Der Erfolg einer Hintergrundinstallation kann nur festgestellt werden, wenn Obtainium geöffnet wird.",
|
"backgroundUpdateLimitsExplanation": "Der Erfolg einer Hintergrundinstallation kann nur festgestellt werden, wenn Obtainium geöffnet wird.",
|
||||||
"verifyLatestTag": "Überprüfe das „latest“ Tag",
|
"verifyLatestTag": "Überprüfe das „latest“ Tag",
|
||||||
|
"intermediateLinkRegex": "Filter für einen „Zwischen“-Link, der zuerst besucht werden soll",
|
||||||
|
"filterByLinkText": "Filtere Links durch Linktext",
|
||||||
|
"intermediateLinkNotFound": "„Zwischen“-Link nicht gefunden",
|
||||||
|
"intermediateLink": "„Zwischen“-Link",
|
||||||
"exemptFromBackgroundUpdates": "Ausschluss von Hintergrundaktualisierungen (falls aktiviert)",
|
"exemptFromBackgroundUpdates": "Ausschluss von Hintergrundaktualisierungen (falls aktiviert)",
|
||||||
"bgUpdatesOnWiFiOnly": "Hintergrundaktualisierungen deaktivieren, wenn kein WLAN vorhanden ist",
|
"bgUpdatesOnWiFiOnly": "Hintergrundaktualisierungen deaktivieren, wenn kein WLAN vorhanden ist",
|
||||||
"autoSelectHighestVersionCode": "Automatisch höchste APK-Code-Version auswählen",
|
"autoSelectHighestVersionCode": "Automatisch höchste APK-Version auswählen",
|
||||||
"versionExtractionRegEx": "Versions-Extraktion RegEx",
|
"versionExtractionRegEx": "Versions-Extraktion per RegEx",
|
||||||
"matchGroupToUse": "Zu verwendende Gruppe abgleichen",
|
"matchGroupToUse": "zu verwendende Gruppe abgleichen",
|
||||||
"highlightTouchTargets": "Weniger offensichtliche Ziele hervorheben",
|
"highlightTouchTargets": "Weniger offensichtliche Touch-Ziele hervorheben",
|
||||||
"pickExportDir": "Export-Verzeichnis wählen",
|
"pickExportDir": "Export-Verzeichnis wählen",
|
||||||
"autoExportOnChanges": "Automatischer Export bei Änderung",
|
"autoExportOnChanges": "Automatischer Export bei Änderung(en)",
|
||||||
|
"includeSettings": "Einstellungen einbeziehen",
|
||||||
"filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern",
|
"filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern",
|
||||||
"trySelectingSuggestedVersionCode": "Versuchen, die vorgeschlagene APK-Code-Version auszuwählen",
|
"trySelectingSuggestedVersionCode": "Versuchen, den vorgeschlagenen APK-Versionscode auszuwählen",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Freigaberelease von der API ordern",
|
||||||
"reverseSort": "Reverse sorting",
|
"reverseSort": "Umgekehrtes Sortieren",
|
||||||
"debugMenu": "Debug Menu",
|
"takeFirstLink": "Verwende den ersten Link",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"skipSort": "Überspringe Sortieren",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"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 auf Aktualisierungen",
|
||||||
|
"supportFixedAPKURL": "neuere Version anhand der ersten dreißig Zahlen der Checksumme der APK URL erraten, wenn anderweitig nicht unterstützt",
|
||||||
|
"selectX": "Wähle {}",
|
||||||
|
"parallelDownloads": "Erlaube parallele Downloads",
|
||||||
|
"installMethod": "Installationsmethode",
|
||||||
|
"normal": "Normal",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Kompatibler Shizukudienst wurde nicht gefunden",
|
||||||
|
"useSystemFont": "Verwende die Systemschriftart",
|
||||||
|
"systemFontError": "Fehler beim Laden der Systemschriftart: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Verwende die Appversion als erkannte Version vom Betriebssystem",
|
||||||
|
"requestHeader": "Request Header",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Den letzten Asset-Upload als Veröffentlichungsdatum verwenden",
|
||||||
|
"defaultPseudoVersioningMethod": "Standardmäßiges Verfahren zur Pseudo-Versionierung",
|
||||||
|
"partialAPKHash": "partieller APK-Hash",
|
||||||
|
"APKLinkHash": "APK-Link-Hash",
|
||||||
|
"directAPKLink": "Direkter APK-Link",
|
||||||
|
"pseudoVersionInUse": "Pseudoversionen sind in Benutzung",
|
||||||
|
"installed": "Installiert",
|
||||||
|
"latest": "Neueste Version",
|
||||||
|
"invertRegEx": "Regulären Ausdruck invertieren",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "App entfernen?",
|
"one": "App entfernen?",
|
||||||
"other": "Apps entfernen?"
|
"other": "Apps entfernen?"
|
||||||
},
|
},
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut",
|
"one": "Zu viele Anfragen (Rate begrenzt) – versuche es in {} Minute erneut",
|
||||||
"other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten erneut"
|
"other": "Zu viele Anfragen (Rate begrenzt) – versuche es in {} Minuten erneut"
|
||||||
},
|
},
|
||||||
"bgUpdateGotErrorRetryInMinutes": {
|
"bgUpdateGotErrorRetryInMinutes": {
|
||||||
"one": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minute geplant",
|
"one": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minute geplant",
|
||||||
"other": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minuten geplant"
|
"other": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minuten geplant"
|
||||||
},
|
},
|
||||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||||
"one": "Hintergrundaktualisierungsprüfung fand {} Aktualisierung - benachrichtigt den Benutzer, falls erforderlich",
|
"one": "Die Hintergrundaktualisierungsprüfung fand {} Aktualisierung – benachrichtigt den Benutzer, falls erforderlich",
|
||||||
"other": "Hintergrundaktualisierungsprüfung fand {} Aktualisierungen - benachrichtigt den Benutzer, falls erforderlich"
|
"other": "Die Hintergrundaktualisierungsprüfung fand {} Aktualisierungen – benachrichtigt den Benutzer, falls erforderlich"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"one": "{} App",
|
"eine": "{} App",
|
||||||
"other": "{} Apps"
|
"andere": "{} Apps"
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"one": "{} URL",
|
"eine": "{} URL",
|
||||||
"other": "{} URLs"
|
"andere": "{} URLs"
|
||||||
},
|
},
|
||||||
"minute": {
|
"minute": {
|
||||||
"one": "{} Minute",
|
"one": "{} Minute",
|
||||||
@@ -297,8 +337,8 @@
|
|||||||
"other": "{} Tage"
|
"other": "{} Tage"
|
||||||
},
|
},
|
||||||
"clearedNLogsBeforeXAfterY": {
|
"clearedNLogsBeforeXAfterY": {
|
||||||
"one": "{n} Protokoll gelöscht (vorher = {vorher}, nachher = {nachher})",
|
"one": "{n} Log gelöscht (vorher = {before}, nachher = {after})",
|
||||||
"other": "{n} Protokolle gelöscht (vorher = {vorher}, nachher = {nachher})"
|
"other": "{n} Logs gelöscht (vorher = {before}, nachher = {after})"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesAvailable": {
|
"xAndNMoreUpdatesAvailable": {
|
||||||
"one": "{} und 1 weitere App haben Aktualisierungen.",
|
"one": "{} und 1 weitere App haben Aktualisierungen.",
|
||||||
|
|||||||
@@ -51,11 +51,10 @@
|
|||||||
"percentProgress": "Progress: {}%",
|
"percentProgress": "Progress: {}%",
|
||||||
"pleaseWait": "Please Wait",
|
"pleaseWait": "Please Wait",
|
||||||
"updateAvailable": "Update Available",
|
"updateAvailable": "Update Available",
|
||||||
"estimateInBracketsShort": "(Est.)",
|
|
||||||
"notInstalled": "Not Installed",
|
"notInstalled": "Not Installed",
|
||||||
"estimateInBrackets": "(Estimate)",
|
"pseudoVersion": "pseudo-version",
|
||||||
"selectAll": "Select All",
|
"selectAll": "Select All",
|
||||||
"deselectN": "Deselect {}",
|
"deselectX": "Deselect {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} will be removed from Obtainium but remain installed on device.",
|
"xWillBeRemovedButRemainInstalled": "{} will be removed from Obtainium but remain installed on device.",
|
||||||
"removeSelectedAppsQuestion": "Remove Selected Apps?",
|
"removeSelectedAppsQuestion": "Remove Selected Apps?",
|
||||||
"removeSelectedApps": "Remove Selected Apps",
|
"removeSelectedApps": "Remove Selected Apps",
|
||||||
@@ -73,6 +72,8 @@
|
|||||||
"unpinFromTop": "Unpin from top",
|
"unpinFromTop": "Unpin from top",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "Reset Install Status for Selected Apps?",
|
"resetInstallStatusForSelectedAppsQuestion": "Reset Install Status for Selected Apps?",
|
||||||
"installStatusOfXWillBeResetExplanation": "The install status of any selected Apps will be reset.\n\nThis can help when the App version shown in Obtainium is incorrect due to failed updates or other issues.",
|
"installStatusOfXWillBeResetExplanation": "The install status of any selected Apps will be reset.\n\nThis can help when the App version shown in Obtainium is incorrect due to failed updates or other issues.",
|
||||||
|
"customLinkMessage": "These links work on devices with Obtainium installed",
|
||||||
|
"shareAppConfigLinks": "Share app configuration as HTML link",
|
||||||
"shareSelectedAppURLs": "Share Selected App URLs",
|
"shareSelectedAppURLs": "Share Selected App URLs",
|
||||||
"resetInstallStatus": "Reset Install Status",
|
"resetInstallStatus": "Reset Install Status",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
@@ -103,7 +104,6 @@
|
|||||||
"importErrors": "Import Errors",
|
"importErrors": "Import Errors",
|
||||||
"importedXOfYApps": "{} of {} Apps imported.",
|
"importedXOfYApps": "{} of {} Apps imported.",
|
||||||
"followingURLsHadErrors": "The following URLs had errors:",
|
"followingURLsHadErrors": "The following URLs had errors:",
|
||||||
"okay": "Okay",
|
|
||||||
"selectURL": "Select URL",
|
"selectURL": "Select URL",
|
||||||
"selectURLs": "Select URLs",
|
"selectURLs": "Select URLs",
|
||||||
"pick": "Pick",
|
"pick": "Pick",
|
||||||
@@ -111,8 +111,6 @@
|
|||||||
"dark": "Dark",
|
"dark": "Dark",
|
||||||
"light": "Light",
|
"light": "Light",
|
||||||
"followSystem": "Follow System",
|
"followSystem": "Follow System",
|
||||||
"obtainium": "Obtainium",
|
|
||||||
"materialYou": "Material You",
|
|
||||||
"useBlackTheme": "Use pure black dark theme",
|
"useBlackTheme": "Use pure black dark theme",
|
||||||
"appSortBy": "App Sort By",
|
"appSortBy": "App Sort By",
|
||||||
"authorName": "Author/Name",
|
"authorName": "Author/Name",
|
||||||
@@ -165,8 +163,8 @@
|
|||||||
"unknown": "Unknown",
|
"unknown": "Unknown",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"never": "Never",
|
"never": "Never",
|
||||||
"latestVersionX": "Latest Version: {}",
|
"latestVersionX": "Latest: {}",
|
||||||
"installedVersionX": "Installed Version: {}",
|
"installedVersionX": "Installed: {}",
|
||||||
"lastUpdateCheckX": "Last Update Check: {}",
|
"lastUpdateCheckX": "Last Update Check: {}",
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
"yesMarkUpdated": "Yes, Mark as Updated",
|
"yesMarkUpdated": "Yes, Mark as Updated",
|
||||||
@@ -176,7 +174,6 @@
|
|||||||
"appWithIdOrNameNotFound": "No App was found with that ID or Name",
|
"appWithIdOrNameNotFound": "No App was found with that ID or Name",
|
||||||
"reposHaveMultipleApps": "Repos may contain multiple Apps",
|
"reposHaveMultipleApps": "Repos may contain multiple Apps",
|
||||||
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
|
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
|
||||||
"steam": "Steam",
|
|
||||||
"steamMobile": "Steam Mobile",
|
"steamMobile": "Steam Mobile",
|
||||||
"steamChat": "Steam Chat",
|
"steamChat": "Steam Chat",
|
||||||
"install": "Install",
|
"install": "Install",
|
||||||
@@ -207,11 +204,12 @@
|
|||||||
"removeFromObtainium": "Remove from Obtainium",
|
"removeFromObtainium": "Remove from Obtainium",
|
||||||
"uninstallFromDevice": "Uninstall from Device",
|
"uninstallFromDevice": "Uninstall from Device",
|
||||||
"onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.",
|
"onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.",
|
||||||
"releaseDateAsVersion": "Use Release Date as Version",
|
"releaseDateAsVersion": "Use release date as version string",
|
||||||
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
|
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
|
||||||
"changes": "Changes",
|
"changes": "Changes",
|
||||||
"releaseDate": "Release Date",
|
"releaseDate": "Release Date",
|
||||||
"importFromURLsInFile": "Import from URLs in File (like OPML)",
|
"importFromURLsInFile": "Import from URLs in File (like OPML)",
|
||||||
|
"versionDetectionExplanation": "Reconcile version string with version detected from OS",
|
||||||
"versionDetection": "Version Detection",
|
"versionDetection": "Version Detection",
|
||||||
"standardVersionDetection": "Standard version detection",
|
"standardVersionDetection": "Standard version detection",
|
||||||
"groupByCategory": "Group by Category",
|
"groupByCategory": "Group by Category",
|
||||||
@@ -223,7 +221,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
|
"requiresCredentialsInSettings": "{} needs additional credentials (in Settings)",
|
||||||
"checkOnStart": "Check for updates on startup",
|
"checkOnStart": "Check for updates on startup",
|
||||||
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
||||||
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
||||||
@@ -236,7 +234,7 @@
|
|||||||
"addInfoInSettings": "Add this info in the Settings.",
|
"addInfoInSettings": "Add this info in the Settings.",
|
||||||
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
|
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
|
||||||
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
|
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
|
||||||
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
|
"sortByLastLinkSegment": "Sort by only the last segment of the link",
|
||||||
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
|
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
|
||||||
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
|
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
|
||||||
"appsPossiblyUpdated": "App Updates Attempted",
|
"appsPossiblyUpdated": "App Updates Attempted",
|
||||||
@@ -246,23 +244,62 @@
|
|||||||
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
|
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
|
||||||
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
|
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
|
||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"verifyLatestTag": "Verify the 'latest' tag",
|
||||||
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
|
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit",
|
||||||
|
"filterByLinkText": "Filter links by link text",
|
||||||
"intermediateLinkNotFound": "Intermediate link not found",
|
"intermediateLinkNotFound": "Intermediate link not found",
|
||||||
|
"intermediateLink": "Intermediate link",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
||||||
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
"versionExtractionRegEx": "Version String Extraction RegEx",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"matchGroupToUse": "Match Group to Use for Version String Extraction Regex",
|
||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"highlightTouchTargets": "Highlight less obvious touch targets",
|
||||||
"pickExportDir": "Pick Export Directory",
|
"pickExportDir": "Pick Export Directory",
|
||||||
"autoExportOnChanges": "Auto-export on changes",
|
"autoExportOnChanges": "Auto-export on changes",
|
||||||
|
"includeSettings": "Include settings",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Retain release order from API",
|
||||||
"reverseSort": "Reverse sorting",
|
"reverseSort": "Reverse sorting",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Debug Menu",
|
"debugMenu": "Debug Menu",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"bgTaskStarted": "Background task started - check logs.",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"runBgCheckNow": "Run Background Update Check Now",
|
||||||
|
"versionExtractWholePage": "Apply version string 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",
|
||||||
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
|
"selectX": "Select {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Сompatible Shizuku service wasn't found",
|
||||||
|
"useSystemFont": "Use the system font",
|
||||||
|
"systemFontError": "Error loading the system font: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version",
|
||||||
|
"requestHeader": "Request header",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date",
|
||||||
|
"defaultPseudoVersioningMethod": "Default Pseudo-Versioning Method",
|
||||||
|
"partialAPKHash": "Partial APK Hash",
|
||||||
|
"APKLinkHash": "APK Link Hash",
|
||||||
|
"directAPKLink": "Direct APK Link",
|
||||||
|
"pseudoVersionInUse": "A Pseudo-Version is in Use",
|
||||||
|
"installed": "Installed",
|
||||||
|
"latest": "Latest",
|
||||||
|
"invertRegEx": "Invert regular expression",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remove App?",
|
"one": "Remove App?",
|
||||||
"other": "Remove Apps?"
|
"other": "Remove Apps?"
|
||||||
@@ -315,4 +352,4 @@
|
|||||||
"one": "{} and 1 more app may have been updated.",
|
"one": "{} and 1 more app may have been updated.",
|
||||||
"other": "{} and {} more apps may have been updated."
|
"other": "{} and {} more apps may have been updated."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,87 +1,88 @@
|
|||||||
{
|
{
|
||||||
"invalidURLForSource": "URL de la aplicación {} no válida",
|
"invalidURLForSource": "URL de la aplicación {} no es válida",
|
||||||
"noReleaseFound": "No se ha podido encontrar una versión válida",
|
"noReleaseFound": "No se ha podido encontrar una versión válida",
|
||||||
"noVersionFound": "No se ha podido determinar la versión de la publicación",
|
"noVersionFound": "No se ha podido determinar la versión",
|
||||||
"urlMatchesNoSource": "La URL no coincide con ninguna fuente conocida",
|
"urlMatchesNoSource": "La URL no coincide con ninguna fuente conocida",
|
||||||
"cantInstallOlderVersion": "No se puede instalar una versión previa de la aplicación",
|
"cantInstallOlderVersion": "No se puede instalar una versión previa de la aplicación",
|
||||||
"appIdMismatch": "La ID del paquete descargado no coincide con la ID de la aplicación instalada",
|
"appIdMismatch": "La ID del paquete descargado no coincide con la ID de la aplicación instalada",
|
||||||
"functionNotImplemented": "Esta clase no ha implementado esta función",
|
"functionNotImplemented": "Esta clase no ha implementado esta función",
|
||||||
"placeholder": "Espacio reservado",
|
"placeholder": "Espacio reservado",
|
||||||
"someErrors": "Han ocurrido algunos errores",
|
"someErrors": "Han ocurrido algunos errores",
|
||||||
"unexpectedError": "Error Inesperado",
|
"unexpectedError": "Error inesperado",
|
||||||
"ok": "Correcto",
|
"ok": "OK",
|
||||||
"and": "y",
|
"and": "y",
|
||||||
"githubPATLabel": "Token de Acceso Personal de GitHub (Reduce tiempos de espera)",
|
"githubPATLabel": "Token de acceso personal a GitHub\n(reduce tiempos de espera)",
|
||||||
"includePrereleases": "Incluir versiones preliminares",
|
"includePrereleases": "Incluir versiones preliminares",
|
||||||
"fallbackToOlderReleases": "Retorceder a versiones previas",
|
"fallbackToOlderReleases": "Retroceder a versiones previas",
|
||||||
"filterReleaseTitlesByRegEx": "Filtra Títulos de Versiones mediantes Expresiones Regulares",
|
"filterReleaseTitlesByRegEx": "Filtrar por título de versión",
|
||||||
"invalidRegEx": "Expresión regular inválida",
|
"invalidRegEx": "Expresión inválida",
|
||||||
"noDescription": "Sin descripción",
|
"noDescription": "Sin descripción",
|
||||||
"cancel": "Cancelar",
|
"cancel": "Cancelar",
|
||||||
"continue": "Continuar",
|
"continue": "Continuar",
|
||||||
"requiredInBrackets": "(Requerido)",
|
"requiredInBrackets": "(Requerido)",
|
||||||
"dropdownNoOptsError": "ERROR: EL DESPLEGABLE DEBE TENER AL MENOS UNA OPCIÓN",
|
"dropdownNoOptsError": "ERROR: EL DESPLEGABLE DEBE TENER AL MENOS UNA OPCIÓN",
|
||||||
"colour": "Color",
|
"colour": "Color",
|
||||||
"githubStarredRepos": "Repositorios favoritos de GitHub",
|
"githubStarredRepos": "Repositorios favoritos en GitHub",
|
||||||
"uname": "Nombre de usuario",
|
"uname": "Nombre de usuario",
|
||||||
"wrongArgNum": "Número de argumentos provistos inválido",
|
"wrongArgNum": "Número de argumentos provistos inválido",
|
||||||
"xIsTrackOnly": "{} es de 'Solo Seguimiento'",
|
"xIsTrackOnly": "{} es de 'sólo seguimiento'",
|
||||||
"source": "Origen",
|
"source": "Origen",
|
||||||
"app": "Aplicación",
|
"app": "Aplicación",
|
||||||
"appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'Solo Seguimiento'.",
|
"appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'solo seguimiento'.",
|
||||||
"youPickedTrackOnly": "Debes seleccionar la opción de 'Solo Seguimiento'.",
|
"youPickedTrackOnly": "Debe seleccionar la opción de 'solo seguimiento'.",
|
||||||
"trackOnlyAppDescription": "Se monitorizará la aplicación en busca de actualizaciones, pero Obtainium no será capaz de descargarla o acutalizarla.",
|
"trackOnlyAppDescription": "Se hará el seguimiento de actualizaciones para la aplicación, pero Obtainium no será capaz de descargar o actualizarla.",
|
||||||
"cancelled": "Cancelado",
|
"cancelled": "Cancelado",
|
||||||
"appAlreadyAdded": "Aplicación ya añadida",
|
"appAlreadyAdded": "Aplicación añadida anteriormente",
|
||||||
"alreadyUpToDateQuestion": "¿Aplicación ya actualizada?",
|
"alreadyUpToDateQuestion": "¿Aplicación actualizada previamente?",
|
||||||
"addApp": "Añadir Aplicación",
|
"addApp": "Añadir Aplicación",
|
||||||
"appSourceURL": "URL de Origen de la Aplicación",
|
"appSourceURL": "URL de origen de la aplicación",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"add": "Añadir",
|
"add": "Añadir",
|
||||||
"searchSomeSourcesLabel": "Buscar (Solo Algunas Fuentes)",
|
"searchSomeSourcesLabel": "Buscar (solo algunas fuentes)",
|
||||||
"search": "Buscar",
|
"search": "Buscar",
|
||||||
"additionalOptsFor": "Opciones Adicionales para {}",
|
"additionalOptsFor": "Opciones Adicionales para {}",
|
||||||
"supportedSources": "Fuentes Soportadas",
|
"supportedSources": "Fuentes Soportadas",
|
||||||
"trackOnlyInBrackets": "(Solo Seguimiento)",
|
"trackOnlyInBrackets": "(Solo seguimiento)",
|
||||||
"searchableInBrackets": "(Soporta Búsquedas)",
|
"searchableInBrackets": "(soporta búsqueda)",
|
||||||
"appsString": "Aplicaciones",
|
"appsString": "Aplicaciones",
|
||||||
"noApps": "Sin Aplicaciones",
|
"noApps": "Sin Aplicaciones",
|
||||||
"noAppsForFilter": "Sin Aplicaciones para Filtrar",
|
"noAppsForFilter": "Sin aplicaciones para filtrar",
|
||||||
"byX": "Por {}",
|
"byX": "por: {}",
|
||||||
"percentProgress": "Progreso: {}%",
|
"percentProgress": "Progreso: {}%",
|
||||||
"pleaseWait": "Por favor, espere",
|
"pleaseWait": "Por favor, espere",
|
||||||
"updateAvailable": "Actualización Disponible",
|
"updateAvailable": "Actualización Disponible",
|
||||||
"estimateInBracketsShort": "(Aprox.)",
|
|
||||||
"notInstalled": "No Instalado",
|
"notInstalled": "No Instalado",
|
||||||
"estimateInBrackets": "(Aproximado)",
|
"pseudoVersion": "pseudoversión",
|
||||||
"selectAll": "Seleccionar Todo",
|
"selectAll": "Seleccionar Todo",
|
||||||
"deselectN": "Deseleccionar {}",
|
"deselectX": "Deseleccionar {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} será borrada de Obtainium pero continuará instalada en el dispositivo.",
|
"xWillBeRemovedButRemainInstalled": "{} será eliminada de Obtainium pero continuará instalada en el dispositivo.",
|
||||||
"removeSelectedAppsQuestion": "¿Borrar aplicaciones seleccionadas?",
|
"removeSelectedAppsQuestion": "¿Eliminar aplicaciones seleccionadas?",
|
||||||
"removeSelectedApps": "Borrar Aplicaciones Seleccionadas",
|
"removeSelectedApps": "Eliminar Aplicaciones Seleccionadas",
|
||||||
"updateX": "Actualizar {}",
|
"updateX": "Actualizar {}",
|
||||||
"installX": "Instalar {}",
|
"installX": "Instalar {}",
|
||||||
"markXTrackOnlyAsUpdated": "Marcar {}\n(Solo Seguimient)\ncomo Actualizada",
|
"markXTrackOnlyAsUpdated": "Marcar {}\n(Solo seguimiento)\ncomo actualizada",
|
||||||
"changeX": "Cambiar {}",
|
"changeX": "Cambiar {}",
|
||||||
"installUpdateApps": "Instalar/Actualizar Aplicaciones",
|
"installUpdateApps": "Instalar/Actualizar aplicaciones",
|
||||||
"installUpdateSelectedApps": "Instalar/Actualizar Aplicaciones Seleccionadas",
|
"installUpdateSelectedApps": "Instalar/Actualizar aplicaciones seleccionadas",
|
||||||
"markXSelectedAppsAsUpdated": "¿Marcar {} Aplicaciones Seleccionadas como Actualizadas?",
|
"markXSelectedAppsAsUpdated": "¿Marcar {} aplicaciones seleccionadas como actualizadas?",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"yes": "Sí",
|
"yes": "Sí",
|
||||||
"markSelectedAppsUpdated": "Marcar Aplicaciones Seleccionadas como Actualizadas",
|
"markSelectedAppsUpdated": "Marcar aplicaciones seleccionadas como actualizadas",
|
||||||
"pinToTop": "Fijar arriba",
|
"pinToTop": "Fijar arriba",
|
||||||
"unpinFromTop": "Desfijar de arriba",
|
"unpinFromTop": "Desfijar de arriba",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "¿Restuarar Estado de Instalación para las Aplicaciones Seleccionadas?",
|
"resetInstallStatusForSelectedAppsQuestion": "¿Restuarar estado de instalación para las aplicaciones seleccionadas?",
|
||||||
"installStatusOfXWillBeResetExplanation": "El estado de instalación de las aplicaciones seleccionadas será restaurado.\n\nEsto puede ser de utilidad cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.",
|
"installStatusOfXWillBeResetExplanation": "Se restaurará el estado de instalación de las aplicaciones seleccionadas.\n\nEsto puede ser de útil cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.",
|
||||||
"shareSelectedAppURLs": "Compartir URLs de las Aplicaciones Seleccionadas",
|
"customLinkMessage": "Estos enlaces funcionan en dispositivos con Obtainium instalado",
|
||||||
"resetInstallStatus": "Restaurar Estado de Instalación",
|
"shareAppConfigLinks": "Compartir la configuración de la aplicación como enlace HTML",
|
||||||
|
"shareSelectedAppURLs": "Compartir URLs de las aplicaciones seleccionadas",
|
||||||
|
"resetInstallStatus": "Restaurar estado de instalación",
|
||||||
"more": "Más",
|
"more": "Más",
|
||||||
"removeOutdatedFilter": "Elimiar Filtro de Aplicaciones Desactualizado",
|
"removeOutdatedFilter": "Elimiar filtro de aplicaciones desactualizado",
|
||||||
"showOutdatedOnly": "Mostrar solo Aplicaciones Desactualizadas",
|
"showOutdatedOnly": "Mostrar solo aplicaciones desactualizadas",
|
||||||
"filter": "Filtrar",
|
"filter": "Filtrar",
|
||||||
"filterActive": "Filtrar *",
|
"filterActive": "Filtrar *",
|
||||||
"filterApps": "Filtrar Actualizaciones",
|
"filterApps": "Filtrar Actualizaciones",
|
||||||
"appName": "Nombre de la Aplicación",
|
"appName": "Nombre de la aplicación",
|
||||||
"author": "Autor",
|
"author": "Autor",
|
||||||
"upToDateApps": "Aplicaciones Actualizadas",
|
"upToDateApps": "Aplicaciones Actualizadas",
|
||||||
"nonInstalledApps": "Aplicaciones No Instaladas",
|
"nonInstalledApps": "Aplicaciones No Instaladas",
|
||||||
@@ -98,168 +99,207 @@
|
|||||||
"line": "Línea",
|
"line": "Línea",
|
||||||
"searchX": "Buscar {}",
|
"searchX": "Buscar {}",
|
||||||
"noResults": "Resultados no encontrados",
|
"noResults": "Resultados no encontrados",
|
||||||
"importX": "Importar {}",
|
"importX": "Importar desde {}",
|
||||||
"importedAppsIdDisclaimer": "Las Aplicaciones Importadas pueden mostrarse incorrectamente como \"No Instalada\".\nPara arreglar esto, reinstálalas a través de Obtainium.\nEsto no debería afectar a los datos de las aplicaciones.\n\nSolo afecta a las URLs y a los métodos de importación mediante terceros.",
|
"importedAppsIdDisclaimer": "Las aplicaciones importadas podrían mostrarse incorrectamente como \"No Instalada\".\nPara solucionarlo, reinstálalas a través de Obtainium.\nEsto no debería afectar a los datos de las aplicaciones.\n\nSolo afecta a las URLs y a los métodos de importación mediante terceros.",
|
||||||
"importErrors": "Import Errors",
|
"importErrors": "Errores de Importación",
|
||||||
"importedXOfYApps": "{} de {} Aplicaciones importadas.",
|
"importedXOfYApps": "{} de {} Aplicaciones importadas.",
|
||||||
"followingURLsHadErrors": "Las siguientes URLs tuvieron problemas:",
|
"followingURLsHadErrors": "Las siguientes URLs han tenido problemas:",
|
||||||
"okay": "Correcto",
|
|
||||||
"selectURL": "Seleccionar URL",
|
"selectURL": "Seleccionar URL",
|
||||||
"selectURLs": "Seleccionar URLs",
|
"selectURLs": "Seleccionar URLs",
|
||||||
"pick": "Escoger",
|
"pick": "Escoger",
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"dark": "Oscuro",
|
"dark": "Oscuro",
|
||||||
"light": "Claro",
|
"light": "Claro",
|
||||||
"followSystem": "Seguir al Sistema",
|
"followSystem": "Seguir al sistema",
|
||||||
"obtainium": "Obtainium",
|
"useBlackTheme": "Negro puro en tema Oscuro",
|
||||||
"materialYou": "Material You",
|
"appSortBy": "Ordenar Apps Por",
|
||||||
"useBlackTheme": "Usar tema oscuro con negros puros",
|
|
||||||
"appSortBy": "Ordenar Aplicaciones Por",
|
|
||||||
"authorName": "Autor/Nombre",
|
"authorName": "Autor/Nombre",
|
||||||
"nameAuthor": "Nombre/Autor",
|
"nameAuthor": "Nombre/Autor",
|
||||||
"asAdded": "Según se Añadieron",
|
"asAdded": "Según se Añadieron",
|
||||||
"appSortOrder": "Orden de Clasificación de Aplicaciones",
|
"appSortOrder": "Orden de Clasificación",
|
||||||
"ascending": "Ascendente",
|
"ascending": "Ascendente",
|
||||||
"descending": "Descendente",
|
"descending": "Descendente",
|
||||||
"bgUpdateCheckInterval": "Intervalo de Comprobación de Actualizaciones en Segundo Plano",
|
"bgUpdateCheckInterval": "Comprobación actualizaciones en segundo plano",
|
||||||
"neverManualOnly": "Nunca - Solo Manual",
|
"neverManualOnly": "Nunca, solo manual",
|
||||||
"appearance": "Apariencia",
|
"appearance": "Apariencia",
|
||||||
"showWebInAppView": "Mostrar Vista de la Web de Origen",
|
"showWebInAppView": "Mostrar vista de la web de origen",
|
||||||
"pinUpdates": "Fijar Actualizaciones en la Parte Superior de la Vista de Aplicaciones",
|
"pinUpdates": "Fijar actualizaciones al principio",
|
||||||
"updates": "Actualizaciones",
|
"updates": "Actualizaciones",
|
||||||
"sourceSpecific": "Fuente Específica",
|
"sourceSpecific": "Fuente Específica",
|
||||||
"appSource": "Fuente de la Aplicación",
|
"appSource": "Obtainium en GitHub",
|
||||||
"noLogs": "Sin Logs",
|
"noLogs": "Sin Logs",
|
||||||
"appLogs": "Logs de la Aplicación",
|
"appLogs": "Logs",
|
||||||
"close": "Cerrar",
|
"close": "Cerrar",
|
||||||
"share": "Compartir",
|
"share": "Compartir",
|
||||||
"appNotFound": "Aplicación no encontrada",
|
"appNotFound": "Aplicación no encontrada",
|
||||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
"obtainiumExportHyphenatedLowercase": "obtainium-exportación",
|
||||||
"pickAnAPK": "Elige una APK",
|
"pickAnAPK": "Seleccione una APK",
|
||||||
"appHasMoreThanOnePackage": "{} tiene más de un paquete:",
|
"appHasMoreThanOnePackage": "{} tiene más de un paquete:",
|
||||||
"deviceSupportsXArch": "Tu dispositivo soporta las siguientes arquitecturas de procesador: {}.",
|
"deviceSupportsXArch": "Su dispositivo soporta las siguientes arquitecturas de procesador: {}.",
|
||||||
"deviceSupportsFollowingArchs": "Tu dispositivo soporta las siguientes arquitecturas de procesador:",
|
"deviceSupportsFollowingArchs": "Su dispositivo soporta las siguientes arquitecturas de procesador:",
|
||||||
"warning": "Aviso",
|
"warning": "Aviso",
|
||||||
"sourceIsXButPackageFromYPrompt": "La fuente de la aplicación es '{}' pero el paquete de la actualización viene de '{}'. ¿Desea continuar?",
|
"sourceIsXButPackageFromYPrompt": "La fuente de la aplicación es '{}' pero el paquete de la actualización viene de '{}'. ¿Desea continuar?",
|
||||||
"updatesAvailable": "Actualizaciones Disponibles",
|
"updatesAvailable": "Actualizaciones Disponibles",
|
||||||
"updatesAvailableNotifDescription": "Notifica al usuario de que hay actualizaciones para una o más aplicaciones monitorizadas por Obtainium",
|
"updatesAvailableNotifDescription": "Notifica al usuario de que hay actualizaciones para una o más aplicaciones monitoreadas por Obtainium",
|
||||||
"noNewUpdates": "No hay nuevas actualizaciones.",
|
"noNewUpdates": "No hay nuevas actualizaciones.",
|
||||||
"xHasAnUpdate": "{} tiene una actualización.",
|
"xHasAnUpdate": "{} tiene una actualización.",
|
||||||
"appsUpdated": "Aplicaciones Actualizadas",
|
"appsUpdated": "Aplicaciones actualizadas",
|
||||||
"appsUpdatedNotifDescription": "Notifica al usuario de que una o más aplicaciones han sido actualizadas en segundo plano",
|
"appsUpdatedNotifDescription": "Notifica al usuario de que una o más aplicaciones han sido actualizadas en segundo plano",
|
||||||
"xWasUpdatedToY": "{} ha sido actualizada a {}.",
|
"xWasUpdatedToY": "{} ha sido actualizada a {}.",
|
||||||
"errorCheckingUpdates": "Error Buscando Actualizaciones",
|
"errorCheckingUpdates": "Error buscando ectualizaciones",
|
||||||
"errorCheckingUpdatesNotifDescription": "Una notificación que muestra cuándo la comprobación de actualizaciones en segundo plano falla",
|
"errorCheckingUpdatesNotifDescription": "Una notificación que muestra cuándo la comprobación de actualizaciones en segundo plano falla",
|
||||||
"appsRemoved": "Aplicaciones Eliminadas",
|
"appsRemoved": "Aplicaciones eliminadas",
|
||||||
"appsRemovedNotifDescription": "Notifica al usuario que una o más aplicaciones fueron eliminadas por problemas al cargarlas",
|
"appsRemovedNotifDescription": "Notifica al usuario que una o más aplicaciones fueron eliminadas por problemas al cargarlas",
|
||||||
"xWasRemovedDueToErrorY": "{} ha sido eliminada por: {}",
|
"xWasRemovedDueToErrorY": "{} ha sido eliminada por: {}",
|
||||||
"completeAppInstallation": "Instalación Completa de la Aplicación",
|
"completeAppInstallation": "Instalación completa de la aplicación",
|
||||||
"obtainiumMustBeOpenToInstallApps": "Obtainium debe estar abierta para instalar aplicaciones",
|
"obtainiumMustBeOpenToInstallApps": "Obtainium debe estar abierto para instalar aplicaciones",
|
||||||
"completeAppInstallationNotifDescription": "Pide al usuario volver a Obtainium para teminar de instalar una aplicación",
|
"completeAppInstallationNotifDescription": "Pide al usuario volver a Obtainium para terminar de instalar una aplicación",
|
||||||
"checkingForUpdates": "Buscando Actualizaciones",
|
"checkingForUpdates": "Buscando actualizaciones...",
|
||||||
"checkingForUpdatesNotifDescription": "Notificación temporal que aparece al buscar actualizaciones",
|
"checkingForUpdatesNotifDescription": "Notificación temporal que aparece al buscar actualizaciones",
|
||||||
"pleaseAllowInstallPerm": "Por favor, permite a Obtainium instalar aplicaciones",
|
"pleaseAllowInstallPerm": "Por favor, permita que Obtainium instale aplicaciones",
|
||||||
"trackOnly": "Solo Seguimiento",
|
"trackOnly": "Solo para seguimiento",
|
||||||
"errorWithHttpStatusCode": "Error {}",
|
"errorWithHttpStatusCode": "Error {}",
|
||||||
"versionCorrectionDisabled": "Corrección de versiones desactivada (el plugin parece no funcionar)",
|
"versionCorrectionDisabled": "Corrección de versiones desactivada (el plugin parece no funcionar)",
|
||||||
"unknown": "Desconocido",
|
"unknown": "Desconocido",
|
||||||
"none": "Ninguno",
|
"none": "Ninguno",
|
||||||
"never": "Nunca",
|
"never": "Nunca",
|
||||||
"latestVersionX": "Última Versión: {}",
|
"latestVersionX": "Última versión: {}",
|
||||||
"installedVersionX": "Versión Instalada: {}",
|
"installedVersionX": "Versión instalada: {}",
|
||||||
"lastUpdateCheckX": "Última Comprobación: {}",
|
"lastUpdateCheckX": "Última comprobación: {}",
|
||||||
"remove": "Eliminar",
|
"remove": "Eliminar",
|
||||||
"yesMarkUpdated": "Sí, Marcar como Actualizada",
|
"yesMarkUpdated": "Sí, marcar como actualizada",
|
||||||
"fdroid": "Repositorio oficial de F-Droid",
|
"fdroid": "Repositorio oficial F-Droid",
|
||||||
"appIdOrName": "ID o Nombre de la Aplicación",
|
"appIdOrName": "ID o Nombre de la Aplicación",
|
||||||
"appId": "ID de la Aplicación",
|
"appId": "ID de la Aplicación",
|
||||||
"appWithIdOrNameNotFound": "No se han encontrado aplicaciones con esa ID o nombre",
|
"appWithIdOrNameNotFound": "No se han encontrado aplicaciones con esa ID o nombre",
|
||||||
"reposHaveMultipleApps": "Los repositorios pueden contener varias aplicaciones",
|
"reposHaveMultipleApps": "Los repositorios pueden contener varias aplicaciones",
|
||||||
"fdroidThirdPartyRepo": "Rpositorios de terceros de F-Droid",
|
"fdroidThirdPartyRepo": "Repositorio de tercera parte F-Droid",
|
||||||
"steam": "Steam",
|
"steamMobile": "Móvil de vapor",
|
||||||
"steamMobile": "Steam Mobile",
|
"steamChat": "Chat de vapor",
|
||||||
"steamChat": "Steam Chat",
|
|
||||||
"install": "Instalar",
|
"install": "Instalar",
|
||||||
"markInstalled": "Marcar como Instalda",
|
"markInstalled": "Marcar como instalada",
|
||||||
"update": "Actualizar",
|
"update": "Actualizar",
|
||||||
"markUpdated": "Marcar como Actualizada",
|
"markUpdated": "Marcar como actualizada",
|
||||||
"additionalOptions": "Opciones Adicionales",
|
"additionalOptions": "Opciones adicionales",
|
||||||
"disableVersionDetection": "Descativar Detección de Versiones",
|
"disableVersionDetection": "Desactivar la detección de versiones",
|
||||||
"noVersionDetectionExplanation": "Esta opción solo se debe usar en aplicaciones en las que la deteción de versiones pueda no funcionar correctamente.",
|
"noVersionDetectionExplanation": "Esta opción solo se debe usar en aplicaciones en las que la deteción de versiones pueda que no funcionar correctamente.",
|
||||||
"downloadingX": "Descargando {}",
|
"downloadingX": "Descargando {}",
|
||||||
"downloadNotifDescription": "Notifica al usuario de progreso de descarga de una aplicación",
|
"downloadNotifDescription": "Notifica al usuario del progreso de descarga de una aplicación",
|
||||||
"noAPKFound": "APK no encontrada",
|
"noAPKFound": "No se encontró el paquete de instalación APK",
|
||||||
"noVersionDetection": "Sin detección de versiones",
|
"noVersionDetection": "Sin detección de versiones",
|
||||||
"categorize": "Catogorizar",
|
"categorize": "Catogorizar",
|
||||||
"categories": "Categorías",
|
"categories": "Categorías",
|
||||||
"category": "Categoría",
|
"category": "Categoría",
|
||||||
"noCategory": "Sin Categoría",
|
"noCategory": "Sin categoría",
|
||||||
"noCategories": "Sin Categorías",
|
"noCategories": "Sin categorías",
|
||||||
"deleteCategoriesQuestion": "¿Borrar Categorías?",
|
"deleteCategoriesQuestion": "¿Eliminar categorías?",
|
||||||
"categoryDeleteWarning": "Todas las aplicaciones en las categorías borradas serán margadas como 'Sin Categoría'.",
|
"categoryDeleteWarning": "Todas las aplicaciones en las categorías eliminadas serán marcadas como 'Sin categoría'.",
|
||||||
"addCategory": "Añadir Categoría",
|
"addCategory": "Añadir categoría",
|
||||||
"label": "Nombre",
|
"label": "Nombre",
|
||||||
"language": "Idioma",
|
"language": "Idioma",
|
||||||
"copiedToClipboard": "Copiado al Portapapeles",
|
"copiedToClipboard": "Copiado al portapapeles",
|
||||||
"storagePermissionDenied": "Permiso de Almacenamiento rechazado",
|
"storagePermissionDenied": "Permiso de almacenamiento rechazado",
|
||||||
"selectedCategorizeWarning": "Esto reemplazará cualquier ajuste de categoría para las aplicaicones seleccionadas.",
|
"selectedCategorizeWarning": "Esto reemplazará cualquier ajuste de categoría para las aplicaciones seleccionadas.",
|
||||||
"filterAPKsByRegEx": "Filtrar APKs mediante Expresiones Regulares",
|
"filterAPKsByRegEx": "Filtrar por APKs",
|
||||||
"removeFromObtainium": "Eliminar de Obtainium",
|
"removeFromObtainium": "Eliminar de Obtainium",
|
||||||
"uninstallFromDevice": "Desinstalar del Dispositivo",
|
"uninstallFromDevice": "Desinstalar del dispositivo",
|
||||||
"onlyWorksWithNonVersionDetectApps": "Solo funciona para aplicaciones con la detección de versiones desactivada.",
|
"onlyWorksWithNonVersionDetectApps": "Solo funciona para aplicaciones con la detección de versiones desactivada.",
|
||||||
"releaseDateAsVersion": "Usar Fecha de Publicación como Versión",
|
"releaseDateAsVersion": "Por fecha de publicación",
|
||||||
"releaseDateAsVersionExplanation": "Esta opción solo se debería usar con aplicaciones en las que la detección de versiones no funciona pero hay disponible una fecha de publicación.",
|
"releaseDateAsVersionExplanation": "Esta opción solo se debería usar con aplicaciones en las que la detección de versiones no funciona pero hay disponible una fecha de publicación.",
|
||||||
"changes": "Cambios",
|
"changes": "Cambios",
|
||||||
"releaseDate": "Fecha de Publicación",
|
"releaseDate": "Fecha de publicación",
|
||||||
"importFromURLsInFile": "Importar de URls en un Archivo (como OPML)",
|
"importFromURLsInFile": "Importar URLs desde archivo (como OPML)",
|
||||||
"versionDetection": "Detección de Versiones",
|
"versionDetectionExplanation": "Conciliar la cadena de versión con la versión detectada desde el sistema operativo",
|
||||||
"standardVersionDetection": "Detección de versiones estándar",
|
"versionDetection": "Detección de versiones",
|
||||||
"groupByCategory": "Agrupar por Categoría",
|
"standardVersionDetection": "Por versión",
|
||||||
"autoApkFilterByArch": "Tratar de filtrar las APKs mediante arquitecturas de procesador si es posible",
|
"groupByCategory": "Agrupar por categoría",
|
||||||
|
"autoApkFilterByArch": "Filtrar APKs por arquitectura del procesador (si es posible)",
|
||||||
"overrideSource": "Sobrescribir Fuente",
|
"overrideSource": "Sobrescribir Fuente",
|
||||||
"dontShowAgain": "No mostrar de nuevo",
|
"dontShowAgain": "No mostrar de nuevo",
|
||||||
"dontShowTrackOnlyWarnings": "No mostrar avisos de 'Solo Seguimiento'",
|
"dontShowTrackOnlyWarnings": "No mostrar avisos sobre apps en 'solo seguimiento'",
|
||||||
"dontShowAPKOriginWarnings": "No mostrar avisos de las fuentes de las APks",
|
"dontShowAPKOriginWarnings": "No mostrar avisos sobre las fuentes de las APKs",
|
||||||
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
"moveNonInstalledAppsToBottom": "Mover Apps no instaladas al final",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
"gitlabPATLabel": "Token de acceso personal a GitLab\n(habilita la búsqueda y mejor detección de APKs)",
|
||||||
"about": "About",
|
"about": "Acerca",
|
||||||
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
|
"requiresCredentialsInSettings": "{}: Esto requiere credenciales adicionales (en ajustes)",
|
||||||
"checkOnStart": "Check for updates on startup",
|
"checkOnStart": "Comprobar actualizaciones al inicio",
|
||||||
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
"tryInferAppIdFromCode": "Intentar deducir la ID de la app por el código fuente",
|
||||||
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
"removeOnExternalUninstall": "Auto eliminar apps desinstaladas externamente",
|
||||||
"pickHighestVersionCode": "Auto-select highest version code APK",
|
"pickHighestVersionCode": "Auto selección de versión superior del paquete APK",
|
||||||
"checkUpdateOnDetailPage": "Check for updates on opening an App detail page",
|
"checkUpdateOnDetailPage": "Comprobar actualizaciones al abrir detalles de la app",
|
||||||
"disablePageTransitions": "Disable page transition animations",
|
"disablePageTransitions": "Deshabilitar animaciones de transición",
|
||||||
"reversePageTransitions": "Reverse page transition animations",
|
"reversePageTransitions": "Invertir animaciones de transición",
|
||||||
"minStarCount": "Minimum Star Count",
|
"minStarCount": "Número Mínimo de Estrellas",
|
||||||
"addInfoBelow": "Add this info below.",
|
"addInfoBelow": "Añadir esta información debajo.",
|
||||||
"addInfoInSettings": "Add this info in the Settings.",
|
"addInfoInSettings": "Puede añadir esta información en Ajustes.",
|
||||||
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
|
"githubSourceNote": "La limitación de velocidad de GitHub puede evitarse con un 'token de acceso personal'.",
|
||||||
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
|
"gitlabSourceNote": "La extracción de APK de GitLab podría no funcionar sin un 'token de acceso personal'.",
|
||||||
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
|
"sortByLastLinkSegment": "Ordenar sólo por el último segmento del enlace",
|
||||||
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
|
"filterReleaseNotesByRegEx": "Filtrar por notas de versión (release notes)",
|
||||||
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
|
"customLinkFilterRegex": "Filtro personalizado de enlace APK (por defecto '.apk$')",
|
||||||
"appsPossiblyUpdated": "App Updates Attempted",
|
"appsPossiblyUpdated": "Actualización de apps intentada",
|
||||||
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
|
"appsPossiblyUpdatedNotifDescription": "Notifica al usuario que las actualizaciones en segundo plano podrían haberse realizado para una o más aplicaciones",
|
||||||
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
|
"xWasPossiblyUpdatedToY": "{} podría estar actualizada a {}.",
|
||||||
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
|
"enableBackgroundUpdates": "Habilitar actualizaciones en segundo plano",
|
||||||
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
|
"backgroundUpdateReqsExplanation": "Las actualizaciones en segundo plano pueden no estar disponibles para todas las aplicaciones.",
|
||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"backgroundUpdateLimitsExplanation": "El éxito de las instalaciones en segundo plano solo se puede comprobar con Obtainium abierto.",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"verifyLatestTag": "Comprobar la etiqueta 'Latest'",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"intermediateLinkRegex": "Filtrar por enlace 'intermedio' para visitar primero",
|
||||||
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
"filterByLinkText": "Filtrar enlaces por texto del enlace",
|
||||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
"intermediateLinkNotFound": "Enlace intermedio no encontrado",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"intermediateLink": "Enlace intermedio",
|
||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"exemptFromBackgroundUpdates": "Exenta de actualizciones en segundo plano (si están habilitadas)",
|
||||||
"pickExportDir": "Pick Export Directory",
|
"bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sin WiFi",
|
||||||
"autoExportOnChanges": "Auto-export on changes",
|
"autoSelectHighestVersionCode": "Auto selección del paquete APK con versión más reciente",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"versionExtractionRegEx": "Versión de extracción regex",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"matchGroupToUse": "Grupo a usar para versión de extracción regex",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"highlightTouchTargets": "Resaltar objetivos menos obvios",
|
||||||
"reverseSort": "Reverse sorting",
|
"pickExportDir": "Directorio para exportar",
|
||||||
"debugMenu": "Debug Menu",
|
"autoExportOnChanges": "Auto exportar cuando haya cambios",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"includeSettings": "Incluir ajustes",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"filterVersionsByRegEx": "Filtrar por versiones",
|
||||||
|
"trySelectingSuggestedVersionCode": "Pruebe seleccionando la versión del paquete APK sugerida",
|
||||||
|
"dontSortReleasesList": "Mantener el order de publicación de la fuente original",
|
||||||
|
"reverseSort": "Orden inverso",
|
||||||
|
"takeFirstLink": "Usar primer enlace",
|
||||||
|
"skipSort": "Omitir orden",
|
||||||
|
"debugMenu": "Menu Depurar",
|
||||||
|
"bgTaskStarted": "Iniciada tarea en segundo plano - revisa los logs.",
|
||||||
|
"runBgCheckNow": "Ejecutar verficiación de actualizaciones en segundo plano",
|
||||||
|
"versionExtractWholePage": "Aplicar la versión de extracción regex a la página entera",
|
||||||
|
"installing": "Instalando",
|
||||||
|
"skipUpdateNotifications": "No notificar sobre actualizaciones",
|
||||||
|
"updatesAvailableNotifChannel": "Actualizaciones disponibles",
|
||||||
|
"appsUpdatedNotifChannel": "Aplicaciones actualizadas",
|
||||||
|
"appsPossiblyUpdatedNotifChannel": "Se ha intentado actualizar la aplicación",
|
||||||
|
"errorCheckingUpdatesNotifChannel": "Error buscando actualizaciones",
|
||||||
|
"appsRemovedNotifChannel": "Aplicaciones eliminadas",
|
||||||
|
"downloadingXNotifChannel": "Descargando {}",
|
||||||
|
"completeAppInstallationNotifChannel": "Instalación completada",
|
||||||
|
"checkingForUpdatesNotifChannel": "Buscando actualizaciones",
|
||||||
|
"onlyCheckInstalledOrTrackOnlyApps": "Comprobar actualizaciones solo para apps instaladas o en seguimiento",
|
||||||
|
"supportFixedAPKURL": "Soporte para URLs fijas de APK",
|
||||||
|
"selectX": "Selecciona {}",
|
||||||
|
"parallelDownloads": "Permitir descargas paralelas",
|
||||||
|
"installMethod": "Método de instalación",
|
||||||
|
"normal": "Normal",
|
||||||
|
"root": "Raíz",
|
||||||
|
"shizukuBinderNotFound": "Shizuku no funciona",
|
||||||
|
"useSystemFont": "Usar la fuente de impresión del sistema",
|
||||||
|
"systemFontError": "Error al cargar la fuente de impresión del sistema: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Usar la versión de la aplicación como versión detectada por el sistema operativo",
|
||||||
|
"requestHeader": "Encabezado de solicitud",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Usar la última carga de recursos como fecha de lanzamiento",
|
||||||
|
"defaultPseudoVersioningMethod": "Método de pseudoversionado predeterminado",
|
||||||
|
"partialAPKHash": "Hash de APK parcial",
|
||||||
|
"APKLinkHash": "Hash de enlace APK",
|
||||||
|
"directAPKLink": "Enlace APK directo",
|
||||||
|
"pseudoVersionInUse": "Se está utilizando una pseudoversión",
|
||||||
|
"installed": "Instalado",
|
||||||
|
"latest": "Versión más reciente",
|
||||||
|
"invertRegEx": "Invertir expresión regular",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "¿Eliminar Aplicación?",
|
"one": "¿Eliminar Aplicación?",
|
||||||
"other": "¿Eliminar Aplicaciones?"
|
"other": "¿Eliminar Aplicaciones?"
|
||||||
@@ -281,35 +321,35 @@
|
|||||||
"other": "{} Aplicaciones"
|
"other": "{} Aplicaciones"
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"one": "{} URL",
|
"uno": "{} URL",
|
||||||
"other": "{} URLs"
|
"otro": "{} URL"
|
||||||
},
|
},
|
||||||
"minute": {
|
"minute": {
|
||||||
"one": "{} Minuto",
|
"one": "{} minuto",
|
||||||
"other": "{} Minutos"
|
"other": "{} minutos"
|
||||||
},
|
},
|
||||||
"hour": {
|
"hour": {
|
||||||
"one": "{} Hora",
|
"one": "{} hora",
|
||||||
"other": "{} Horas"
|
"other": "{} horas"
|
||||||
},
|
},
|
||||||
"day": {
|
"day": {
|
||||||
"one": "{} Día",
|
"one": "{} día",
|
||||||
"other": "{} Días"
|
"other": "{} días"
|
||||||
},
|
},
|
||||||
"clearedNLogsBeforeXAfterY": {
|
"clearedNLogsBeforeXAfterY": {
|
||||||
"one": "Borrado {n} log (previo a = {before}, posterior a = {after})",
|
"one": "Eliminado {n} log (previo a = {before}, posterior a = {after})",
|
||||||
"other": "Borrados {n} logs (previos a = {before}, posteriores a = {after})"
|
"other": "Eliminados {n} logs (previos a = {before}, posteriores a = {after})"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesAvailable": {
|
"xAndNMoreUpdatesAvailable": {
|
||||||
"one": "{} y 1 aplicación más tiene actualizaciones.",
|
"one": "{} y 1 aplicación más tiene actualizaciones.",
|
||||||
"other": "{} y {} aplicaciones más tiene actualizaciones."
|
"other": "{} y {} aplicaciones más tienen actualizaciones."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesInstalled": {
|
"xAndNMoreUpdatesInstalled": {
|
||||||
"one": "{} y 1 aplicación más han sido actualizadas.",
|
"one": "{} y 1 aplicación más han sido actualizadas.",
|
||||||
"other": "{} y {} aplicaciones más han sido actualizadas."
|
"other": "{} y {} aplicaciones más han sido actualizadas."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
"one": "{} and 1 more app may have been updated.",
|
"one": "{} y 1 aplicación más podría haber sido actualizada.",
|
||||||
"other": "{} and {} more apps may have been updated."
|
"other": "{} y {} aplicaciones más podrían haber sido actualizadas."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,11 +51,10 @@
|
|||||||
"percentProgress": "پیش رفتن: {}%",
|
"percentProgress": "پیش رفتن: {}%",
|
||||||
"pleaseWait": "لطفا صبر کنید",
|
"pleaseWait": "لطفا صبر کنید",
|
||||||
"updateAvailable": "بروزرسانی در دسترس",
|
"updateAvailable": "بروزرسانی در دسترس",
|
||||||
"estimateInBracketsShort": "(تخمین)",
|
|
||||||
"notInstalled": "نصب نشده",
|
"notInstalled": "نصب نشده",
|
||||||
"estimateInBrackets": "(تخمین زدن)",
|
"pseudoVersion": "نسخه شبه",
|
||||||
"selectAll": "انتخاب همه",
|
"selectAll": "انتخاب همه",
|
||||||
"deselectN": "لغو انتخاب {}",
|
"deselectX": "لغو انتخاب {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} از Obtainium حذف میشود اما روی دستگاه نصب میماند.",
|
"xWillBeRemovedButRemainInstalled": "{} از Obtainium حذف میشود اما روی دستگاه نصب میماند.",
|
||||||
"removeSelectedAppsQuestion": "برنامه های انتخابی حذف شود؟",
|
"removeSelectedAppsQuestion": "برنامه های انتخابی حذف شود؟",
|
||||||
"removeSelectedApps": "حذف برنامه های انتخاب شده",
|
"removeSelectedApps": "حذف برنامه های انتخاب شده",
|
||||||
@@ -73,6 +72,8 @@
|
|||||||
"unpinFromTop": "برداشتن پین از بالا",
|
"unpinFromTop": "برداشتن پین از بالا",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "وضعیت نصب برنامههای انتخابی بازنشانی شود؟",
|
"resetInstallStatusForSelectedAppsQuestion": "وضعیت نصب برنامههای انتخابی بازنشانی شود؟",
|
||||||
"installStatusOfXWillBeResetExplanation": "وضعیت نصب برنامههای انتخابشده بازنشانی میشود.\n\nاگر نسخه برنامه نشاندادهشده در Obtainium به دلیل بهروزرسانیهای ناموفق یا مشکلات دیگر نادرست باشد، میتواند کمک کند.",
|
"installStatusOfXWillBeResetExplanation": "وضعیت نصب برنامههای انتخابشده بازنشانی میشود.\n\nاگر نسخه برنامه نشاندادهشده در Obtainium به دلیل بهروزرسانیهای ناموفق یا مشکلات دیگر نادرست باشد، میتواند کمک کند.",
|
||||||
|
"customLinkMessage": "این پیوندها بر روی دستگاه هایی با Obtainium نصب شده کار می کنند",
|
||||||
|
"shareAppConfigLinks": "اشتراک گذاری پیکربندی برنامه به عنوان پیوند HTML",
|
||||||
"shareSelectedAppURLs": "اشتراک گذاری آدرس اینترنتی برنامه های انتخاب شده",
|
"shareSelectedAppURLs": "اشتراک گذاری آدرس اینترنتی برنامه های انتخاب شده",
|
||||||
"resetInstallStatus": "بازنشانی وضعیت نصب",
|
"resetInstallStatus": "بازنشانی وضعیت نصب",
|
||||||
"more": "بیشتر",
|
"more": "بیشتر",
|
||||||
@@ -85,25 +86,24 @@
|
|||||||
"author": "سازنده",
|
"author": "سازنده",
|
||||||
"upToDateApps": "برنامه های به روز",
|
"upToDateApps": "برنامه های به روز",
|
||||||
"nonInstalledApps": "برنامه های نصب نشده",
|
"nonInstalledApps": "برنامه های نصب نشده",
|
||||||
"importExport": "وادر کردن/صادر کردن",
|
"importExport": "درون ریزی/برون ریزی",
|
||||||
"settings": "تنظیمات",
|
"settings": "تنظیمات",
|
||||||
"exportedTo": "صادر کردن به{}",
|
"exportedTo": "برون ریزی به{}",
|
||||||
"obtainiumExport": "صادرکردن Obtainium",
|
"obtainiumExport": "صادرکردن Obtainium",
|
||||||
"invalidInput": "ورودی نامعتبر",
|
"invalidInput": "ورودی نامعتبر",
|
||||||
"importedX": "وارد شده {}",
|
"importedX": "وارد شده {}",
|
||||||
"obtainiumImport": "واردکردن Obtainium",
|
"obtainiumImport": "واردکردن Obtainium",
|
||||||
"importFromURLList": "وارد کردن از فهرست آدرس اینترنتی",
|
"importFromURLList": "درون ریزی از فهرست آدرس اینترنتی",
|
||||||
"searchQuery": "جستجوی سوال",
|
"searchQuery": "جستجوی سوال",
|
||||||
"appURLList": "فهرست آدرس اینترنتی برنامه",
|
"appURLList": "فهرست آدرس اینترنتی برنامه",
|
||||||
"line": "خط",
|
"line": "خط",
|
||||||
"searchX": "جستجو {}",
|
"searchX": "جستجو {}",
|
||||||
"noResults": "نتیجه ای پیدا نشد",
|
"noResults": "نتیجه ای پیدا نشد",
|
||||||
"importX": "وارد کردن {}",
|
"importX": "درون ریزی {}",
|
||||||
"importedAppsIdDisclaimer": "ممکن است برنامههای وارد شده به اشتباه بهعنوان \"نصب نشده\" نشان داده شوند.\nبرای رفع این مشکل، آنها را دوباره از طریق Obtainium نصب کنید.\nاین نباید روی دادههای برنامه تأثیر بگذارد.\n\nفقط بر روی آدرس اینترنتی و روشهای وارد کردن شخص ثالث تأثیر میگذارد.",
|
"importedAppsIdDisclaimer": "ممکن است برنامههای وارد شده به اشتباه بهعنوان \"نصب نشده\" نشان داده شوند.\nبرای رفع این مشکل، آنها را دوباره از طریق Obtainium نصب کنید.\nاین نباید روی دادههای برنامه تأثیر بگذارد.\n\nفقط بر روی آدرس اینترنتی و روشهای درون ریزی شخص ثالث تأثیر میگذارد.",
|
||||||
"importErrors": "خطاهای وارد کردن",
|
"importErrors": "خطاهای درون ریزی",
|
||||||
"importedXOfYApps": "{} از {} برنامه وارد شد.",
|
"importedXOfYApps": "{} از {} برنامه وارد شد.",
|
||||||
"followingURLsHadErrors": "آدرس های اینترنتی زیر دارای خطا بودند:",
|
"followingURLsHadErrors": "آدرس های اینترنتی زیر دارای خطا بودند:",
|
||||||
"okay": "باشه",
|
|
||||||
"selectURL": "آدرس اینترنتی انتخاب شده",
|
"selectURL": "آدرس اینترنتی انتخاب شده",
|
||||||
"selectURLs": "آدرس های اینترنتی انتخاب شده",
|
"selectURLs": "آدرس های اینترنتی انتخاب شده",
|
||||||
"pick": "انتخاب",
|
"pick": "انتخاب",
|
||||||
@@ -111,8 +111,6 @@
|
|||||||
"dark": "تاریک",
|
"dark": "تاریک",
|
||||||
"light": "روشن",
|
"light": "روشن",
|
||||||
"followSystem": "هماهنگ با سیستم",
|
"followSystem": "هماهنگ با سیستم",
|
||||||
"obtainium": "Obtainium",
|
|
||||||
"materialYou": "Material You",
|
|
||||||
"useBlackTheme": "استفاده از تم تیره سیاه خالص",
|
"useBlackTheme": "استفاده از تم تیره سیاه خالص",
|
||||||
"appSortBy": "مرتب سازی برنامه بر اساس",
|
"appSortBy": "مرتب سازی برنامه بر اساس",
|
||||||
"authorName": "سازنده/اسم",
|
"authorName": "سازنده/اسم",
|
||||||
@@ -134,7 +132,7 @@
|
|||||||
"close": "بستن",
|
"close": "بستن",
|
||||||
"share": "اشتراک گذاری",
|
"share": "اشتراک گذاری",
|
||||||
"appNotFound": "برنامه پیدا نشد",
|
"appNotFound": "برنامه پیدا نشد",
|
||||||
"obtainiumExportHyphenatedLowercase": "صادر کردن-obtainium",
|
"obtainiumExportHyphenatedLowercase": "برون ریزی-obtainium",
|
||||||
"pickAnAPK": "یک APK انتخاب کنید",
|
"pickAnAPK": "یک APK انتخاب کنید",
|
||||||
"appHasMoreThanOnePackage": "{} بیش از یک بسته دارد:",
|
"appHasMoreThanOnePackage": "{} بیش از یک بسته دارد:",
|
||||||
"deviceSupportsXArch": "دستگاه شما از معماری پردازنده {} پشتیبانی میکند",
|
"deviceSupportsXArch": "دستگاه شما از معماری پردازنده {} پشتیبانی میکند",
|
||||||
@@ -172,13 +170,12 @@
|
|||||||
"yesMarkUpdated": "بله، علامت گذاری به عنوان به روز شده",
|
"yesMarkUpdated": "بله، علامت گذاری به عنوان به روز شده",
|
||||||
"fdroid": "F-Droid Official",
|
"fdroid": "F-Droid Official",
|
||||||
"appIdOrName": "شناسه یا نام برنامه",
|
"appIdOrName": "شناسه یا نام برنامه",
|
||||||
"appId": "App ID",
|
"appId": "شناسه برنامه",
|
||||||
"appWithIdOrNameNotFound": "هیچ برنامه ای با آن شناسه یا نام یافت نشد",
|
"appWithIdOrNameNotFound": "هیچ برنامه ای با آن شناسه یا نام یافت نشد",
|
||||||
"reposHaveMultipleApps": "مخازن ممکن است شامل چندین برنامه باشد",
|
"reposHaveMultipleApps": "مخازن ممکن است شامل چندین برنامه باشد",
|
||||||
"fdroidThirdPartyRepo": "مخازن شخص ثالث F-Droid",
|
"fdroidThirdPartyRepo": "مخازن شخص ثالث F-Droid",
|
||||||
"steam": "Steam",
|
"steamMobile": "استیم موبایل",
|
||||||
"steamMobile": "Steam Mobile",
|
"steamChat": "چت استیم",
|
||||||
"steamChat": "Steam Chat",
|
|
||||||
"install": "نصب",
|
"install": "نصب",
|
||||||
"markInstalled": "علامت گذاری به عنوان نصب شده",
|
"markInstalled": "علامت گذاری به عنوان نصب شده",
|
||||||
"update": "به روز رسانی",
|
"update": "به روز رسانی",
|
||||||
@@ -211,7 +208,8 @@
|
|||||||
"releaseDateAsVersionExplanation": "این گزینه فقط باید برای برنامه هایی استفاده شود که تشخیص نسخه به درستی کار نمی کند، اما تاریخ انتشار در دسترس است.",
|
"releaseDateAsVersionExplanation": "این گزینه فقط باید برای برنامه هایی استفاده شود که تشخیص نسخه به درستی کار نمی کند، اما تاریخ انتشار در دسترس است.",
|
||||||
"changes": "تغییرات",
|
"changes": "تغییرات",
|
||||||
"releaseDate": "تاریخ انتشار",
|
"releaseDate": "تاریخ انتشار",
|
||||||
"importFromURLsInFile": "وارد کردن از آدرس های اینترنتی موجود در فایل (مانند OPML)",
|
"importFromURLsInFile": "درون ریزی از آدرس های اینترنتی موجود در فایل (مانند OPML)",
|
||||||
|
"versionDetectionExplanation": "تطبیق رشته نسخه با نسخه شناسایی شده از سیستم عامل",
|
||||||
"versionDetection": "تشخیص نسخه",
|
"versionDetection": "تشخیص نسخه",
|
||||||
"standardVersionDetection": "تشخیص نسخه استاندارد",
|
"standardVersionDetection": "تشخیص نسخه استاندارد",
|
||||||
"groupByCategory": "گروه بر اساس دسته",
|
"groupByCategory": "گروه بر اساس دسته",
|
||||||
@@ -223,7 +221,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "برنامه های نصب نشده را به نمای پایین برنامه ها منتقل کنید",
|
"moveNonInstalledAppsToBottom": "برنامه های نصب نشده را به نمای پایین برنامه ها منتقل کنید",
|
||||||
"gitlabPATLabel": "رمز دسترسی شخصی GitLab\n(جستجو و کشف بهتر APK را فعال میکند)",
|
"gitlabPATLabel": "رمز دسترسی شخصی GitLab\n(جستجو و کشف بهتر APK را فعال میکند)",
|
||||||
"about": "درباره",
|
"about": "درباره",
|
||||||
"requiresCredentialsInSettings": "این به اعتبارنامه های اضافی نیاز دارد (در تنظیمات)",
|
"requiresCredentialsInSettings": "{}: این به اعتبارنامه های اضافی نیاز دارد (در تنظیمات)",
|
||||||
"checkOnStart": "بررسی در شروع",
|
"checkOnStart": "بررسی در شروع",
|
||||||
"tryInferAppIdFromCode": "شناسه برنامه را از کد منبع استنباط کنید",
|
"tryInferAppIdFromCode": "شناسه برنامه را از کد منبع استنباط کنید",
|
||||||
"removeOnExternalUninstall": "حذف خودکار برنامه های حذف نصب شده خارجی",
|
"removeOnExternalUninstall": "حذف خودکار برنامه های حذف نصب شده خارجی",
|
||||||
@@ -236,30 +234,72 @@
|
|||||||
"addInfoInSettings": "این اطلاعات را در تنظیمات اضافه کنید.",
|
"addInfoInSettings": "این اطلاعات را در تنظیمات اضافه کنید.",
|
||||||
"githubSourceNote": "با استفاده از کلید API می توان از محدودیت نرخ GitHub جلوگیری کرد.",
|
"githubSourceNote": "با استفاده از کلید API می توان از محدودیت نرخ GitHub جلوگیری کرد.",
|
||||||
"gitlabSourceNote": "استخراج APK GitLab ممکن است بدون کلید API کار نکند.",
|
"gitlabSourceNote": "استخراج APK GitLab ممکن است بدون کلید API کار نکند.",
|
||||||
"sortByFileNamesNotLinks": "مرتب سازی بر اساس نام فایل به جای پیوندهای کامل",
|
"sortByLastLinkSegment": "فقط بر اساس آخرین بخش پیوند مرتب کنید",
|
||||||
"filterReleaseNotesByRegEx": "یادداشت های انتشار را با بیان منظم فیلتر کنید",
|
"filterReleaseNotesByRegEx": "یادداشت های انتشار را با بیان منظم فیلتر کنید",
|
||||||
"customLinkFilterRegex": "فیلتر پیوند سفارشی بر اساس عبارت منظم (پیشفرض '.apk$')",
|
"customLinkFilterRegex": "فیلتر پیوند سفارشی بر اساس عبارت منظم (پیشفرض '.apk$')",
|
||||||
"appsPossiblyUpdated": "App Updates Attempted",
|
"appsPossiblyUpdated": "بهروزرسانی برنامه انجام شد",
|
||||||
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
|
"appsPossiblyUpdatedNotifDescription": "به کاربر اطلاع میدهد که بهروزرسانیهای یک یا چند برنامه به طور بالقوه در پسزمینه اعمال شده است",
|
||||||
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
|
"xWasPossiblyUpdatedToY": "ممکن است {} به {} به روز شده باشد.",
|
||||||
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
|
"enableBackgroundUpdates": "به روز رسانی پس زمینه را فعال کنید",
|
||||||
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
|
"backgroundUpdateReqsExplanation": "به روز رسانی پس زمینه ممکن است برای همه برنامه ها امکان پذیر نباشد.",
|
||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"backgroundUpdateLimitsExplanation": "موفقیت نصب پسزمینه تنها زمانی مشخص میشود که Obtainium باز شود.",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"verifyLatestTag": "برچسب \"آخرین\" را تأیید کنید",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"intermediateLinkRegex": "برای بازدید از پیوند «میانگین» فیلتر کنید",
|
||||||
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
"filterByLinkText": "لینک ها را بر اساس متن پیوند فیلتر کنید",
|
||||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
"intermediateLinkNotFound": "لینک میانی پیدا نشد",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"intermediateLink": "پیوند میانی",
|
||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"exemptFromBackgroundUpdates": "معاف از بهروزرسانیهای پسزمینه (در صورت فعال بودن)",
|
||||||
"pickExportDir": "Pick Export Directory",
|
"bgUpdatesOnWiFiOnly": "بهروزرسانیهای پسزمینه را در صورت عدم اتصال به WiFi غیرفعال کنید",
|
||||||
"autoExportOnChanges": "Auto-export on changes",
|
"autoSelectHighestVersionCode": "انتخاب خودکار بالاترین نسخه کد APK",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"versionExtractionRegEx": "نسخه استخراج RegEx",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"matchGroupToUse": "گروه مورد استفاده را مطابقت دهید",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"highlightTouchTargets": "اهداف لمسی کمتر واضح را برجسته کنید",
|
||||||
"reverseSort": "Reverse sorting",
|
"pickExportDir": "فهرست برون ریزی را انتخاب کنید",
|
||||||
"debugMenu": "Debug Menu",
|
"autoExportOnChanges": "برون ریزی خودکار تغییرات",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"includeSettings": "شامل تنظیمات",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"filterVersionsByRegEx": "فیلتر کردن نسخه ها با RegEx",
|
||||||
|
"trySelectingSuggestedVersionCode": "نسخه پیشنهادی APK نسخه کد را انتخاب کنید",
|
||||||
|
"dontSortReleasesList": "حفظ سفارش انتشار از API",
|
||||||
|
"reverseSort": "مرتب سازی معکوس",
|
||||||
|
"takeFirstLink": "لینک اول را بگیرید",
|
||||||
|
"skipSort": "از مرتب سازی صرف نظر کنید",
|
||||||
|
"debugMenu": "منوی اشکال زدایی",
|
||||||
|
"bgTaskStarted": "کار پس زمینه شروع شد - لاگ های مربوط را بررسی کنید.",
|
||||||
|
"runBgCheckNow": "اکنون بهروزرسانی پسزمینه را بررسی کنید",
|
||||||
|
"versionExtractWholePage": "نسخه Extraction Regex را در کل صفحه اعمال کنید",
|
||||||
|
"installing": "در حال نصب",
|
||||||
|
"skipUpdateNotifications": "رد شدن از اعلان های به روز رسانی",
|
||||||
|
"updatesAvailableNotifChannel": "بروزرسانی در دسترس ",
|
||||||
|
"appsUpdatedNotifChannel": "برنامه ها به روز شدند",
|
||||||
|
"appsPossiblyUpdatedNotifChannel": "بهروزرسانی برنامه انجام شد",
|
||||||
|
"errorCheckingUpdatesNotifChannel": "خطا در بررسی بهروزرسانیها",
|
||||||
|
"appsRemovedNotifChannel": "برنامه ها حذف شدند",
|
||||||
|
"downloadingXNotifChannel": "در حال دانلود {}",
|
||||||
|
"completeAppInstallationNotifChannel": "نصب کامل برنامه",
|
||||||
|
"checkingForUpdatesNotifChannel": "بررسی بهروزرسانیها",
|
||||||
|
"onlyCheckInstalledOrTrackOnlyApps": "فقط برنامه های نصب شده و فقط ردیابی را برای به روز رسانی بررسی کنید",
|
||||||
|
"supportFixedAPKURL": "پشتیبانی از URL های APK ثابت",
|
||||||
|
"selectX": "انتخاب کنید {}",
|
||||||
|
"parallelDownloads": "اجازه دانلود موازی",
|
||||||
|
"installMethod": "روش نصب",
|
||||||
|
"normal": "طبیعی",
|
||||||
|
"root": "ریشه",
|
||||||
|
"shizukuBinderNotFound": "Shizuku در حال اجرا نیست",
|
||||||
|
"useSystemFont": "استفاده از فونت سیستم",
|
||||||
|
"systemFontError": "خطا در بارگیری فونت سیستم: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "استفاده کد نسخه برنامه به جای نسخه شناسایی شده توسط سیستم عامل استفاده کنید",
|
||||||
|
"requestHeader": "درخواست سطر بالایی",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "استفاده از آخرین بارگذاری دارایی به عنوان تاریخ انتشار",
|
||||||
|
"defaultPseudoVersioningMethod": "روش پیش فرض شبه نسخه",
|
||||||
|
"partialAPKHash": "هش جزئی APK",
|
||||||
|
"APKLinkHash": "هش پیوند APK",
|
||||||
|
"directAPKLink": "لینک مستقیم APK",
|
||||||
|
"pseudoVersionInUse": "یک نسخه شبه در حال استفاده است",
|
||||||
|
"installed": "نصب شده است",
|
||||||
|
"latest": "آخرین",
|
||||||
|
"invertRegEx": "معکوس کردن عبارت منظم",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "برنامه حذف شود؟",
|
"one": "برنامه حذف شود؟",
|
||||||
"other": "برنامه ها حذف شوند؟"
|
"other": "برنامه ها حذف شوند؟"
|
||||||
@@ -309,7 +349,7 @@
|
|||||||
"other": "{} و {} برنامه دیگر به روز شدند."
|
"other": "{} و {} برنامه دیگر به روز شدند."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
"one": "{} and 1 more app may have been updated.",
|
"one": "{} و 1 برنامه دیگر ممکن است به روز شده باشند.",
|
||||||
"other": "{} and {} more apps may have been updated."
|
"other": "ممکن است {} و {} برنامه های دیگر به روز شده باشند."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"placeholder": "Espace réservé",
|
"placeholder": "Espace réservé",
|
||||||
"someErrors": "Des erreurs se sont produites",
|
"someErrors": "Des erreurs se sont produites",
|
||||||
"unexpectedError": "Erreur inattendue",
|
"unexpectedError": "Erreur inattendue",
|
||||||
"ok": "Okay",
|
"ok": "D'accord",
|
||||||
"and": "et",
|
"and": "et",
|
||||||
"githubPATLabel": "Jeton d'Accès Personnel GitHub (Augmente la limite de débit)",
|
"githubPATLabel": "Jeton d'Accès Personnel GitHub (Augmente la limite de débit)",
|
||||||
"includePrereleases": "Inclure les avant-premières",
|
"includePrereleases": "Inclure les avant-premières",
|
||||||
@@ -51,11 +51,10 @@
|
|||||||
"percentProgress": "Progrès: {}%",
|
"percentProgress": "Progrès: {}%",
|
||||||
"pleaseWait": "Veuillez patienter",
|
"pleaseWait": "Veuillez patienter",
|
||||||
"updateAvailable": "Mise à jour disponible",
|
"updateAvailable": "Mise à jour disponible",
|
||||||
"estimateInBracketsShort": "(Est.)",
|
|
||||||
"notInstalled": "Pas installé",
|
"notInstalled": "Pas installé",
|
||||||
"estimateInBrackets": "(Estimation)",
|
"pseudoVersion": "pseudo-version",
|
||||||
"selectAll": "Tout sélectionner",
|
"selectAll": "Tout sélectionner",
|
||||||
"deselectN": "Déselectionner {}",
|
"deselectX": "Déselectionner {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} sera supprimé d'Obtainium mais restera installé sur l'appareil.",
|
"xWillBeRemovedButRemainInstalled": "{} sera supprimé d'Obtainium mais restera installé sur l'appareil.",
|
||||||
"removeSelectedAppsQuestion": "Supprimer les applications sélectionnées ?",
|
"removeSelectedAppsQuestion": "Supprimer les applications sélectionnées ?",
|
||||||
"removeSelectedApps": "Supprimer les applications sélectionnées",
|
"removeSelectedApps": "Supprimer les applications sélectionnées",
|
||||||
@@ -73,6 +72,8 @@
|
|||||||
"unpinFromTop": "Détacher du haut",
|
"unpinFromTop": "Détacher du haut",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "Réinitialiser l'état d'installation des applications sélectionnées ?",
|
"resetInstallStatusForSelectedAppsQuestion": "Réinitialiser l'état d'installation des applications sélectionnées ?",
|
||||||
"installStatusOfXWillBeResetExplanation": "L'état d'installation de toutes les applications sélectionnées sera réinitialisé.\n\nCela peut aider lorsque la version de l'application affichée dans Obtainium est incorrecte en raison d'échecs de mises à jour ou d'autres problèmes.",
|
"installStatusOfXWillBeResetExplanation": "L'état d'installation de toutes les applications sélectionnées sera réinitialisé.\n\nCela peut aider lorsque la version de l'application affichée dans Obtainium est incorrecte en raison d'échecs de mises à jour ou d'autres problèmes.",
|
||||||
|
"customLinkMessage": "Ces liens fonctionnent sur les appareils sur lesquels Obtenirium est installé",
|
||||||
|
"shareAppConfigLinks": "Partager la configuration de l'application sous forme de lien HTML",
|
||||||
"shareSelectedAppURLs": "Partager les URL d'application sélectionnées",
|
"shareSelectedAppURLs": "Partager les URL d'application sélectionnées",
|
||||||
"resetInstallStatus": "Réinitialiser le statut d'installation",
|
"resetInstallStatus": "Réinitialiser le statut d'installation",
|
||||||
"more": "Plus",
|
"more": "Plus",
|
||||||
@@ -103,7 +104,6 @@
|
|||||||
"importErrors": "Erreurs d'importation",
|
"importErrors": "Erreurs d'importation",
|
||||||
"importedXOfYApps": "{} sur {} applications importées.",
|
"importedXOfYApps": "{} sur {} applications importées.",
|
||||||
"followingURLsHadErrors": "Les URL suivantes comportaient des erreurs :",
|
"followingURLsHadErrors": "Les URL suivantes comportaient des erreurs :",
|
||||||
"okay": "Okay",
|
|
||||||
"selectURL": "Sélectionnez l'URL",
|
"selectURL": "Sélectionnez l'URL",
|
||||||
"selectURLs": "Sélectionnez les URLs",
|
"selectURLs": "Sélectionnez les URLs",
|
||||||
"pick": "Prendre",
|
"pick": "Prendre",
|
||||||
@@ -111,9 +111,7 @@
|
|||||||
"dark": "Sombre",
|
"dark": "Sombre",
|
||||||
"light": "Clair",
|
"light": "Clair",
|
||||||
"followSystem": "Suivre le système",
|
"followSystem": "Suivre le système",
|
||||||
"obtainium": "Obtainium",
|
"useBlackTheme": "Utilisez le thème noir pur et sombre",
|
||||||
"materialYou": "Material You",
|
|
||||||
"useBlackTheme": "Use pure black dark theme",
|
|
||||||
"appSortBy": "Applications triées par",
|
"appSortBy": "Applications triées par",
|
||||||
"authorName": "Auteur/Nom",
|
"authorName": "Auteur/Nom",
|
||||||
"nameAuthor": "Nom/Auteur",
|
"nameAuthor": "Nom/Auteur",
|
||||||
@@ -134,7 +132,7 @@
|
|||||||
"close": "Fermer",
|
"close": "Fermer",
|
||||||
"share": "Partager",
|
"share": "Partager",
|
||||||
"appNotFound": "Application introuvable",
|
"appNotFound": "Application introuvable",
|
||||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
"obtainiumExportHyphenatedLowercase": "exportation d'obtainium",
|
||||||
"pickAnAPK": "Choisissez un APK",
|
"pickAnAPK": "Choisissez un APK",
|
||||||
"appHasMoreThanOnePackage": "{} a plus d'un paquet :",
|
"appHasMoreThanOnePackage": "{} a plus d'un paquet :",
|
||||||
"deviceSupportsXArch": "Votre appareil prend en charge l'architecture de processeur {}.",
|
"deviceSupportsXArch": "Votre appareil prend en charge l'architecture de processeur {}.",
|
||||||
@@ -170,15 +168,14 @@
|
|||||||
"lastUpdateCheckX": "Vérification de la dernière mise à jour : {}",
|
"lastUpdateCheckX": "Vérification de la dernière mise à jour : {}",
|
||||||
"remove": "Retirer",
|
"remove": "Retirer",
|
||||||
"yesMarkUpdated": "Oui, marquer comme mis à jour",
|
"yesMarkUpdated": "Oui, marquer comme mis à jour",
|
||||||
"fdroid": "F-Droid Official",
|
"fdroid": "F-Droïde Officiel",
|
||||||
"appIdOrName": "ID ou nom de l'application",
|
"appIdOrName": "ID ou nom de l'application",
|
||||||
"appId": "ID de l'application",
|
"appId": "ID de l'application",
|
||||||
"appWithIdOrNameNotFound": "Aucune application n'a été trouvée avec cet identifiant ou ce nom",
|
"appWithIdOrNameNotFound": "Aucune application n'a été trouvée avec cet identifiant ou ce nom",
|
||||||
"reposHaveMultipleApps": "Les dépôts peuvent contenir plusieurs applications",
|
"reposHaveMultipleApps": "Les dépôts peuvent contenir plusieurs applications",
|
||||||
"fdroidThirdPartyRepo": "Dépôt tiers F-Droid",
|
"fdroidThirdPartyRepo": "Dépôt tiers F-Droid",
|
||||||
"steam": "Steam",
|
"steamMobile": "Vapeur Mobile",
|
||||||
"steamMobile": "Steam Mobile",
|
"steamChat": "Chat sur Steam",
|
||||||
"steamChat": "Steam Chat",
|
|
||||||
"install": "Installer",
|
"install": "Installer",
|
||||||
"markInstalled": "Marquer installée",
|
"markInstalled": "Marquer installée",
|
||||||
"update": "Mettre à jour",
|
"update": "Mettre à jour",
|
||||||
@@ -193,14 +190,14 @@
|
|||||||
"categorize": "Catégoriser",
|
"categorize": "Catégoriser",
|
||||||
"categories": "Catégories",
|
"categories": "Catégories",
|
||||||
"category": "Catégorie",
|
"category": "Catégorie",
|
||||||
"noCategory": "No Category",
|
"noCategory": "Aucune catégorie",
|
||||||
"noCategories": "Aucune catégorie",
|
"noCategories": "Aucune catégorie",
|
||||||
"deleteCategoriesQuestion": "Supprimer les catégories ?",
|
"deleteCategoriesQuestion": "Supprimer les catégories ?",
|
||||||
"categoryDeleteWarning": "Toutes les applications dans les catégories supprimées seront définies sur non catégorisées.",
|
"categoryDeleteWarning": "Toutes les applications dans les catégories supprimées seront définies sur non catégorisées.",
|
||||||
"addCategory": "Ajouter une catégorie",
|
"addCategory": "Ajouter une catégorie",
|
||||||
"label": "Étiquette",
|
"label": "Étiquette",
|
||||||
"language": "Langue",
|
"language": "Langue",
|
||||||
"copiedToClipboard": "Copied to Clipboard",
|
"copiedToClipboard": "Copié dans le presse-papier",
|
||||||
"storagePermissionDenied": "Autorisation de stockage refusée",
|
"storagePermissionDenied": "Autorisation de stockage refusée",
|
||||||
"selectedCategorizeWarning": "Cela remplacera tous les paramètres de catégorie existants pour les applications sélectionnées.",
|
"selectedCategorizeWarning": "Cela remplacera tous les paramètres de catégorie existants pour les applications sélectionnées.",
|
||||||
"filterAPKsByRegEx": "Filtrer les APK par expression régulière",
|
"filterAPKsByRegEx": "Filtrer les APK par expression régulière",
|
||||||
@@ -212,54 +209,97 @@
|
|||||||
"changes": "Changements",
|
"changes": "Changements",
|
||||||
"releaseDate": "Date de sortie",
|
"releaseDate": "Date de sortie",
|
||||||
"importFromURLsInFile": "Importer à partir d'URL dans un fichier (comme OPML)",
|
"importFromURLsInFile": "Importer à partir d'URL dans un fichier (comme OPML)",
|
||||||
|
"versionDetectionExplanation": "Réconcilier la chaîne de version avec la version détectée à partir du système d'exploitation",
|
||||||
"versionDetection": "Détection des versions",
|
"versionDetection": "Détection des versions",
|
||||||
"standardVersionDetection": "Détection de version standard",
|
"standardVersionDetection": "Détection de version standard",
|
||||||
"groupByCategory": "Group by Category",
|
"groupByCategory": "Regrouper par catégorie",
|
||||||
"autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible",
|
"autoApkFilterByArch": "Essayez de filtrer les APK par architecture CPU si possible",
|
||||||
"overrideSource": "Override Source",
|
"overrideSource": "Remplacer la source",
|
||||||
"dontShowAgain": "Don't show this again",
|
"dontShowAgain": "Ne montre plus ça",
|
||||||
"dontShowTrackOnlyWarnings": "Don't Show the 'Track-Only' Warning",
|
"dontShowTrackOnlyWarnings": "Don't Show the 'Track-Only' Warning",
|
||||||
"dontShowAPKOriginWarnings": "Don't show APK origin warnings",
|
"dontShowAPKOriginWarnings": "Ne pas afficher les avertissements sur l'origine de l'APK",
|
||||||
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
"moveNonInstalledAppsToBottom": "Déplacer les applications non installées vers le bas de la vue Applications",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
|
"gitlabPATLabel": "Jeton d'accès personnel GitLab\\n (permet la recherche et une meilleure découverte d'APK)",
|
||||||
"about": "About",
|
"about": "À propos de",
|
||||||
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
|
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
|
||||||
"checkOnStart": "Check for updates on startup",
|
"checkOnStart": "Vérifier les mises à jour au démarrage",
|
||||||
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
"tryInferAppIdFromCode": "Essayez de déduire l'ID de l'application à partir du code source",
|
||||||
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
"removeOnExternalUninstall": "Supprimer automatiquement les applications désinstallées en externe",
|
||||||
"pickHighestVersionCode": "Auto-select highest version code APK",
|
"pickHighestVersionCode": "Sélectionner automatiquement le code APK de la version la plus élevée",
|
||||||
"checkUpdateOnDetailPage": "Check for updates on opening an App detail page",
|
"checkUpdateOnDetailPage": "Vérifier les mises à jour lors de l'ouverture d'une page de détails d'application",
|
||||||
"disablePageTransitions": "Disable page transition animations",
|
"disablePageTransitions": "Désactiver les animations de transition de page",
|
||||||
"reversePageTransitions": "Reverse page transition animations",
|
"reversePageTransitions": "Animations de transition de page inversée",
|
||||||
"minStarCount": "Minimum Star Count",
|
"minStarCount": "Nombre minimum d'étoiles",
|
||||||
"addInfoBelow": "Add this info below.",
|
"addInfoBelow": "Ajoutez ces informations ci-dessous.",
|
||||||
"addInfoInSettings": "Add this info in the Settings.",
|
"addInfoInSettings": "Ajoutez ces informations dans les paramètres.",
|
||||||
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
|
"githubSourceNote": "La limitation du débit GitHub peut être évitée à l'aide d'une clé API.",
|
||||||
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
|
"gitlabSourceNote": "L'extraction d'APK GitLab peut ne pas fonctionner sans clé API.",
|
||||||
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
|
"sortByLastLinkSegment": "Trier uniquement sur le dernier segment du lien",
|
||||||
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
|
"filterReleaseNotesByRegEx": "Filtrer les notes de version par expression régulière",
|
||||||
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
|
"customLinkFilterRegex": "Filtre de lien APK personnalisé par expression régulière (par défaut '.apk$')",
|
||||||
"appsPossiblyUpdated": "App Updates Attempted",
|
"appsPossiblyUpdated": "Tentative de mise à jour de l'application",
|
||||||
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
|
"appsPossiblyUpdatedNotifDescription": "Avertit l'utilisateur que des mises à jour d'une ou plusieurs applications ont été potentiellement appliquées en arrière-plan",
|
||||||
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
|
"xWasPossiblyUpdatedToY": "{} a peut-être été mis à jour vers {}.",
|
||||||
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
|
"enableBackgroundUpdates": "Activer les mises à jour en arrière-plan",
|
||||||
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
|
"backgroundUpdateReqsExplanation": "Les mises à jour en arrière-plan peuvent ne pas être possibles pour toutes les applications.",
|
||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"backgroundUpdateLimitsExplanation": "Le succès d'une installation en arrière-plan ne peut être déterminé qu'à l'ouverture d'Obetium.",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"verifyLatestTag": "Vérifiez la balise 'dernière'",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"intermediateLinkRegex": " Filtrer un lien \" intermédiaire \" à visiter ",
|
||||||
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
"filterByLinkText": "Filtrer les liens par texte de lien",
|
||||||
|
"intermediateLinkNotFound": "Lien intermédiaire introuvable",
|
||||||
|
"intermediateLink": "Lien intermédiaire",
|
||||||
|
"exemptFromBackgroundUpdates": "Exempt des mises à jour en arrière-plan (si activé)",
|
||||||
|
"bgUpdatesOnWiFiOnly": "Désactiver les mises à jour en arrière-plan lorsque vous n'êtes pas connecté au WiFi",
|
||||||
|
"autoSelectHighestVersionCode": "Sélection automatique de la version la plus élevéeCode APK",
|
||||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
"versionExtractionRegEx": "Version Extraction RegEx",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"matchGroupToUse": "Match Group to Use",
|
||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"highlightTouchTargets": "Mettez en évidence les cibles tactiles moins évidentes",
|
||||||
"pickExportDir": "Pick Export Directory",
|
"pickExportDir": "Choisir le répertoire d'exportation",
|
||||||
"autoExportOnChanges": "Auto-export on changes",
|
"autoExportOnChanges": "Exportation automatique sur modifications",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"includeSettings": "Inclure les paramètres",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"filterVersionsByRegEx": "Filtrer les versions par expression régulière",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"trySelectingSuggestedVersionCode": "Essayez de sélectionner la version suggéréeCode APK",
|
||||||
"reverseSort": "Reverse sorting",
|
"dontSortReleasesList": "Conserver la commande de version de l'API",
|
||||||
"debugMenu": "Debug Menu",
|
"reverseSort": "Tri inversé",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"takeFirstLink": "Prendre le premier lien",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"skipSort": "Sauter le tri",
|
||||||
|
"debugMenu": "Menu de débogage",
|
||||||
|
"bgTaskStarted": "Tâche en arrière-plan démarrée - vérifier les journaux.",
|
||||||
|
"runBgCheckNow": "Exécuter la vérification de la mise à jour en arrière-plan maintenant",
|
||||||
|
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
|
||||||
|
"installing": "Installation",
|
||||||
|
"skipUpdateNotifications": "Ignorer les notifications de mise à jour",
|
||||||
|
"updatesAvailableNotifChannel": "Mises à jour disponibles",
|
||||||
|
"appsUpdatedNotifChannel": "Applications mises à jour",
|
||||||
|
"appsPossiblyUpdatedNotifChannel": "Tentative de mise à jour de l'application",
|
||||||
|
"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": "Vérifiez uniquement les mises à jour des applications installées et de suivi uniquement",
|
||||||
|
"supportFixedAPKURL": "Prise en charge des URL APK fixes",
|
||||||
|
"selectX": "Sélectionner {}",
|
||||||
|
"parallelDownloads": "Autoriser les téléchargements parallèles",
|
||||||
|
"installMethod": "Méthode d'installation",
|
||||||
|
"normal": "Normale",
|
||||||
|
"root": "Racine",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
|
"useSystemFont": "Utiliser la police système",
|
||||||
|
"systemFontError": "Erreur de chargement de la police système : {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Utiliser le code de version de l'application comme version détectée par le système d'exploitation",
|
||||||
|
"requestHeader": "En-tête de demande",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Utiliser le dernier téléchargement d'élément comme date de sortie",
|
||||||
|
"defaultPseudoVersioningMethod": "Méthode de pseudo-version par défaut",
|
||||||
|
"partialAPKHash": "Hash APK partiel",
|
||||||
|
"APKLinkHash": "Hash de lien APK",
|
||||||
|
"directAPKLink": "Lien APK direct",
|
||||||
|
"pseudoVersionInUse": "Une pseudo-version est utilisée",
|
||||||
|
"installed": "Installée",
|
||||||
|
"latest": "Dernier",
|
||||||
|
"invertRegEx": "Inverser l'expression régulière",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Supprimer l'application ?",
|
"one": "Supprimer l'application ?",
|
||||||
"other": "Supprimer les applications ?"
|
"other": "Supprimer les applications ?"
|
||||||
@@ -282,10 +322,10 @@
|
|||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"one": "{} URL",
|
"one": "{} URL",
|
||||||
"other": "{} URLs"
|
"other": "{} URL"
|
||||||
},
|
},
|
||||||
"minute": {
|
"minute": {
|
||||||
"one": "{} Minute",
|
"one": "{} Minutes",
|
||||||
"other": "{} Minutes"
|
"other": "{} Minutes"
|
||||||
},
|
},
|
||||||
"hour": {
|
"hour": {
|
||||||
@@ -309,7 +349,7 @@
|
|||||||
"other": "{} et {} autres applications ont été mises à jour."
|
"other": "{} et {} autres applications ont été mises à jour."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
"one": "{} and 1 more app may have been updated.",
|
"une": "{} et 1 application supplémentaire ont peut-être été mises à jour.",
|
||||||
"other": "{} and {} more apps may have been updated."
|
"other": "{} et {} autres applications peuvent avoir été mises à jour."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,315 +1,355 @@
|
|||||||
{
|
{
|
||||||
"invalidURLForSource": "Érvénytelen a(z) {} app URL-je",
|
"invalidURLForSource": "Érvénytelen a(z) {} app URL-je",
|
||||||
"noReleaseFound": "Nem található megfelelő kiadás",
|
"noReleaseFound": "Nem található megfelelő kiadás",
|
||||||
"noVersionFound": "Nem sikerült meghatározni a kiadás verzióját",
|
"noVersionFound": "Nem sikerült meghatározni a kiadás verzióját",
|
||||||
"urlMatchesNoSource": "Az URL nem egyezik ismert forrással",
|
"urlMatchesNoSource": "Az URL nem egyezik ismert forrással",
|
||||||
"cantInstallOlderVersion": "Nem telepíthető egy app régebbi verziója",
|
"cantInstallOlderVersion": "Nem telepíthető egy app régebbi verziója",
|
||||||
"appIdMismatch": "A letöltött csomagazonosító nem egyezik a meglévő app azonosítóval",
|
"appIdMismatch": "A letöltött csomagazonosító nem egyezik a meglévő app azonosítóval",
|
||||||
"functionNotImplemented": "Ez az osztály nem valósította meg ezt a függvényt",
|
"functionNotImplemented": "Ez az osztály nem valósította meg ezt a függvényt",
|
||||||
"placeholder": "Helykitöltő",
|
"placeholder": "Helykitöltő",
|
||||||
"someErrors": "Néhány hiba történt",
|
"someErrors": "Néhány hiba történt",
|
||||||
"unexpectedError": "Váratlan hiba",
|
"unexpectedError": "Váratlan hiba",
|
||||||
"ok": "Oké",
|
"ok": "Oké",
|
||||||
"and": "és",
|
"and": "és",
|
||||||
"githubPATLabel": "GitHub Personal Access Token (megnöveli a díjkorlátot)",
|
"githubPATLabel": "GitHub Personal Access Token (megnöveli a díjkorlátot)",
|
||||||
"includePrereleases": "Tartalmazza az előzetes kiadásokat",
|
"includePrereleases": "Tartalmazza az előzetes kiadásokat",
|
||||||
"fallbackToOlderReleases": "Visszatérés a régebbi kiadásokhoz",
|
"fallbackToOlderReleases": "Visszatérés a régebbi kiadásokhoz",
|
||||||
"filterReleaseTitlesByRegEx": "A kiadás címeinek szűrése reguláris kifejezéssel",
|
"filterReleaseTitlesByRegEx": "A kiadás címeinek szűrése reguláris kifejezéssel",
|
||||||
"invalidRegEx": "Érvénytelen reguláris kifejezés",
|
"invalidRegEx": "Érvénytelen reguláris kifejezés",
|
||||||
"noDescription": "Nincs leírás",
|
"noDescription": "Nincs leírás",
|
||||||
"cancel": "Mégse",
|
"cancel": "Mégse",
|
||||||
"continue": "Tovább",
|
"continue": "Tovább",
|
||||||
"requiredInBrackets": "(Kötelező)",
|
"requiredInBrackets": "(Kötelező)",
|
||||||
"dropdownNoOptsError": "HIBA: A LEDOBÁST LEGALÁBB EGY OPCIÓHOZ KELL RENDELNI",
|
"dropdownNoOptsError": "HIBA: A LEDOBÁST LEGALÁBB EGY OPCIÓHOZ KELL RENDELNI",
|
||||||
"colour": "Szín",
|
"colour": "Szín",
|
||||||
"githubStarredRepos": "GitHub Csillagos Repo-k",
|
"githubStarredRepos": "GitHub Csillagos Repo-k",
|
||||||
"uname": "Felh.név",
|
"uname": "Felh.név",
|
||||||
"wrongArgNum": "Rossz számú argumentumot adott meg",
|
"wrongArgNum": "Rossz számú argumentumot adott meg",
|
||||||
"xIsTrackOnly": "A(z) {} csak nyomonkövethető",
|
"xIsTrackOnly": "A(z) {} csak nyomonkövethető",
|
||||||
"source": "Forrás",
|
"source": "Forrás",
|
||||||
"app": "App",
|
"app": "Alkalmazás",
|
||||||
"appsFromSourceAreTrackOnly": "Az ebből a forrásból származó alkalmazások 'Csak nyomon követhetőek'.",
|
"appsFromSourceAreTrackOnly": "Az ebből a forrásból származó alkalmazások 'Csak nyomon követhetőek'.",
|
||||||
"youPickedTrackOnly": "A 'Csak követés' opciót választotta.",
|
"youPickedTrackOnly": "A 'Csak követés' opciót választotta.",
|
||||||
"trackOnlyAppDescription": "Az alkalmazás frissítéseit nyomon követi, de az Obtainium nem tudja letölteni vagy telepíteni.",
|
"trackOnlyAppDescription": "Az alkalmazás frissítéseit nyomon követi, de az Obtainium nem tudja letölteni vagy telepíteni.",
|
||||||
"cancelled": "Törölve",
|
"cancelled": "Törölve",
|
||||||
"appAlreadyAdded": "Az app már hozzáadva",
|
"appAlreadyAdded": "Az app már hozzáadva",
|
||||||
"alreadyUpToDateQuestion": "Az app már naprakész?",
|
"alreadyUpToDateQuestion": "Az app már naprakész?",
|
||||||
"addApp": "App hozzáadás",
|
"addApp": "App hozzáadás",
|
||||||
"appSourceURL": "App forrás URL",
|
"appSourceURL": "App forrás URL",
|
||||||
"error": "Hiba",
|
"error": "Hiba",
|
||||||
"add": "Hozzáadás",
|
"add": "Hozzáadás",
|
||||||
"searchSomeSourcesLabel": "Keresés (csak egyes források)",
|
"searchSomeSourcesLabel": "Keresés (csak egyes források)",
|
||||||
"search": "Keresés",
|
"search": "Keresés",
|
||||||
"additionalOptsFor": "További lehetőségek a következőhöz: {}",
|
"additionalOptsFor": "További lehetőségek a következőhöz: {}",
|
||||||
"supportedSources": "Támogatott források",
|
"supportedSources": "Támogatott források",
|
||||||
"trackOnlyInBrackets": "(Csak nyomonkövetés)",
|
"trackOnlyInBrackets": "(Csak nyomonkövetés)",
|
||||||
"searchableInBrackets": "(Kereshető)",
|
"searchableInBrackets": "(Kereshető)",
|
||||||
"appsString": "Appok",
|
"appsString": "Appok",
|
||||||
"noApps": "Nincs App",
|
"noApps": "Nincs App",
|
||||||
"noAppsForFilter": "Nincsenek appok a szűrőhöz",
|
"noAppsForFilter": "Nincsenek appok a szűrőhöz",
|
||||||
"byX": "Fejlesztő: {}",
|
"byX": "Fejlesztő: {}",
|
||||||
"percentProgress": "Folyamat: {}%",
|
"percentProgress": "Folyamat: {}%",
|
||||||
"pleaseWait": "Kis türelmet",
|
"pleaseWait": "Kis türelmet",
|
||||||
"updateAvailable": "Frissítés érhető el",
|
"updateAvailable": "Frissítés érhető el",
|
||||||
"estimateInBracketsShort": "(Becsült)",
|
"notInstalled": "Nem telepített",
|
||||||
"notInstalled": "Nem telepített",
|
"pseudoVersion": "ál-verzió",
|
||||||
"estimateInBrackets": "(Becslés)",
|
"selectAll": "Mindet kiválaszt",
|
||||||
"selectAll": "Mindet kiválaszt",
|
"deselectX": "Törölje {} kijelölését",
|
||||||
"deselectN": "Törölje {} kijelölését",
|
"xWillBeRemovedButRemainInstalled": "A(z) {} el lesz távolítva az Obtainiumból, de továbbra is telepítve marad az eszközön.",
|
||||||
"xWillBeRemovedButRemainInstalled": "A(z) {} el lesz távolítva az Obtainiumból, de továbbra is telepítve marad az eszközön.",
|
"removeSelectedAppsQuestion": "Eltávolítja a kiválasztott appokat?",
|
||||||
"removeSelectedAppsQuestion": "Eltávolítja a kiválasztott appokat?",
|
"removeSelectedApps": "Távolítsa el a kiválasztott appokat",
|
||||||
"removeSelectedApps": "Távolítsa el a kiválasztott appokat",
|
"updateX": "Frissítés: {}",
|
||||||
"updateX": "Frissítés: {}",
|
"installX": "Telepítés: {}",
|
||||||
"installX": "Telepítés: {}",
|
"markXTrackOnlyAsUpdated": "Jelölje meg: {}\n(Csak nyomon követhető)\nmint Frissített",
|
||||||
"markXTrackOnlyAsUpdated": "Jelölje meg: {}\n(Csak nyomon követhető)\nmint Frissített",
|
"changeX": "Változás {}",
|
||||||
"changeX": "Változás {}",
|
"installUpdateApps": "Appok telepítése/frissítése",
|
||||||
"installUpdateApps": "Appok telepítése/frissítése",
|
"installUpdateSelectedApps": "Telepítse/frissítse a kiválasztott appokat",
|
||||||
"installUpdateSelectedApps": "Telepítse/frissítse a kiválasztott appokat",
|
"markXSelectedAppsAsUpdated": "Megjelöl {} kiválasztott alkalmazást frissítettként?",
|
||||||
"markXSelectedAppsAsUpdated": "Megjelöl {} kiválasztott alkalmazást frissítettként?",
|
"no": "Nem",
|
||||||
"no": "Nem",
|
"yes": "Igen",
|
||||||
"yes": "Igen",
|
"markSelectedAppsUpdated": "Jelölje meg a kiválasztott appokat frissítettként",
|
||||||
"markSelectedAppsUpdated": "Jelölje meg a kiválasztott appokat frissítettként",
|
"pinToTop": "Rögzítés felülre",
|
||||||
"pinToTop": "Rögzítés felülre",
|
"unpinFromTop": "Eltávolít felülről",
|
||||||
"unpinFromTop": "Eltávolít felülről",
|
"resetInstallStatusForSelectedAppsQuestion": "Visszaállítja a kiválasztott appok telepítési állapotát?",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "Visszaállítja a kiválasztott appok telepítési állapotát?",
|
"installStatusOfXWillBeResetExplanation": "A kiválasztott appok telepítési állapota visszaáll.\n\nEz akkor segíthet, ha az Obtainiumban megjelenített app verzió hibás, frissítések vagy egyéb problémák miatt.",
|
||||||
"installStatusOfXWillBeResetExplanation": "A kiválasztott appok telepítési állapota visszaáll.\n\nEz akkor segíthet, ha az Obtainiumban megjelenített app verzió hibás, frissítések vagy egyéb problémák miatt.",
|
"customLinkMessage": "Ezek a hivatkozások a telepített Obtainium-mal rendelkező eszközökön működnek",
|
||||||
"shareSelectedAppURLs": "Ossza meg a kiválasztott app URL címeit",
|
"shareAppConfigLinks": "App konfiguráció megosztása HTML linkként",
|
||||||
"resetInstallStatus": "Telepítési állapot visszaállítása",
|
"shareSelectedAppURLs": "Ossza meg a kiválasztott app URL címeit",
|
||||||
"more": "További",
|
"resetInstallStatus": "Telepítési állapot visszaállítása",
|
||||||
"removeOutdatedFilter": "Távolítsa el az elavult app szűrőt",
|
"more": "További",
|
||||||
"showOutdatedOnly": "Csak az elavult appok megjelenítése",
|
"removeOutdatedFilter": "Távolítsa el az elavult app szűrőt",
|
||||||
"filter": "Szűrő",
|
"showOutdatedOnly": "Csak az elavult appok megjelenítése",
|
||||||
"filterActive": "Szűrő *",
|
"filter": "Szűrő",
|
||||||
"filterApps": "Appok szűrése",
|
"filterActive": "Szűrő *",
|
||||||
"appName": "App név",
|
"filterApps": "Appok szűrése",
|
||||||
"author": "Szerző",
|
"appName": "App név",
|
||||||
"upToDateApps": "Naprakész appok",
|
"author": "Szerző",
|
||||||
"nonInstalledApps": "Nem telepített appok",
|
"upToDateApps": "Naprakész appok",
|
||||||
"importExport": "Import/Export",
|
"nonInstalledApps": "Nem telepített appok",
|
||||||
"settings": "Beállítások",
|
"importExport": "Import Export",
|
||||||
"exportedTo": "Exportálva ide {}",
|
"settings": "Beállítások",
|
||||||
"obtainiumExport": "Obtainium Adat Exportálás",
|
"exportedTo": "Exportálva ide {}",
|
||||||
"invalidInput": "Hibás bemenet",
|
"obtainiumExport": "Obtainium Adat Exportálás",
|
||||||
"importedX": "Importálva innen {}",
|
"invalidInput": "Hibás bemenet",
|
||||||
"obtainiumImport": "Obtainium Adat Importálás",
|
"importedX": "Importálva innen {}",
|
||||||
"importFromURLList": "Importálás URL listából",
|
"obtainiumImport": "Obtainium Adat Importálás",
|
||||||
"searchQuery": "Keresési lekérdezés",
|
"importFromURLList": "Importálás URL listából",
|
||||||
"appURLList": "App URL lista",
|
"searchQuery": "Keresési lekérdezés",
|
||||||
"line": "Sor",
|
"appURLList": "App URL lista",
|
||||||
"searchX": "Keresés {}",
|
"line": "Sor",
|
||||||
"noResults": "Nincs találat",
|
"searchX": "Keresés {}",
|
||||||
"importX": "Import {}",
|
"noResults": "Nincs találat",
|
||||||
"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..",
|
"importX": "Importálás: {}",
|
||||||
"importErrors": "Importálási hibák",
|
"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..",
|
||||||
"importedXOfYApps": "{}/{} app importálva.",
|
"importErrors": "Importálási hibák",
|
||||||
"followingURLsHadErrors": "A következő URL-ek hibákat tartalmaztak:",
|
"importedXOfYApps": "{}/{} app importálva.",
|
||||||
"okay": "Oké",
|
"followingURLsHadErrors": "A következő URL-ek hibákat tartalmaztak:",
|
||||||
"selectURL": "Válassza ki az URL-t",
|
"selectURL": "Válassza ki az URL-t",
|
||||||
"selectURLs": "Kiválasztott URL-ek",
|
"selectURLs": "Kiválasztott URL-ek",
|
||||||
"pick": "Válasszon",
|
"pick": "Válasszon",
|
||||||
"theme": "Téma",
|
"theme": "Téma",
|
||||||
"dark": "Sötét",
|
"dark": "Sötét",
|
||||||
"light": "Világos",
|
"light": "Világos",
|
||||||
"followSystem": "Rendszer szerint",
|
"followSystem": "Rendszer szerint",
|
||||||
"obtainium": "Obtainium",
|
"useBlackTheme": "Használjon teljesen fekete sötét témát",
|
||||||
"materialYou": "Material You",
|
"appSortBy": "App rendezés...",
|
||||||
"useBlackTheme": "Használjon tiszta fekete sötét témát",
|
"authorName": "Szerző/Név",
|
||||||
"appSortBy": "App rendezés...",
|
"nameAuthor": "Név/Szerző",
|
||||||
"authorName": "Szerző/Név",
|
"asAdded": "Mint Hozzáadott",
|
||||||
"nameAuthor": "Név/Szerző",
|
"appSortOrder": "Appok rendezése",
|
||||||
"asAdded": "Mint Hozzáadott",
|
"ascending": "Emelkedő",
|
||||||
"appSortOrder": "Appok rendezése",
|
"descending": "Csökkenő",
|
||||||
"ascending": "Emelkedő",
|
"bgUpdateCheckInterval": "Háttérfrissítés ellenőrzés időköze",
|
||||||
"descending": "Csökkenő",
|
"neverManualOnly": "Soha – csak manuális",
|
||||||
"bgUpdateCheckInterval": "Háttérfrissítés ellenőrzés időköze",
|
"appearance": "Megjelenés",
|
||||||
"neverManualOnly": "Soha – csak manuális",
|
"showWebInAppView": "Forrás megjelenítése az Appok nézetben",
|
||||||
"appearance": "Megjelenés",
|
"pinUpdates": "Frissítések kitűzése az App nézet tetejére",
|
||||||
"showWebInAppView": "Forrás megjelenítése az Appok nézetben",
|
"updates": "Frissítések",
|
||||||
"pinUpdates": "Frissítések kitűzése az App nézet tetejére",
|
"sourceSpecific": "Forrás-specifikus",
|
||||||
"updates": "Frissítések",
|
"appSource": "App forrás",
|
||||||
"sourceSpecific": "Forrás-specifikus",
|
"noLogs": "Nincsenek naplók",
|
||||||
"appSource": "App forrás",
|
"appLogs": "App naplók",
|
||||||
"noLogs": "Nincsenek naplók",
|
"close": "Bezárás",
|
||||||
"appLogs": "App naplók",
|
"share": "Megosztás",
|
||||||
"close": "Bezárás",
|
"appNotFound": "App nem található",
|
||||||
"share": "Megosztás",
|
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
||||||
"appNotFound": "App nem található",
|
"pickAnAPK": "Válasszon egy APK-t",
|
||||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
"appHasMoreThanOnePackage": "A(z) {} egynél több csomaggal rendelkezik:",
|
||||||
"pickAnAPK": "Válasszon egy APK-t",
|
"deviceSupportsXArch": "Eszköze támogatja a {} CPU architektúrát.",
|
||||||
"appHasMoreThanOnePackage": "A(z) {} egynél több csomaggal rendelkezik:",
|
"deviceSupportsFollowingArchs": "Az eszköze a következő CPU architektúrákat támogatja:",
|
||||||
"deviceSupportsXArch": "Eszköze támogatja a {} CPU architektúrát.",
|
"warning": "Figyelem",
|
||||||
"deviceSupportsFollowingArchs": "Az eszköze a következő CPU architektúrákat támogatja:",
|
"sourceIsXButPackageFromYPrompt": "Az alkalmazás forrása „{}”, de a kiadási csomag innen származik: „{}”. Folytatja?",
|
||||||
"warning": "Figyelem",
|
"updatesAvailable": "Frissítések érhetők el",
|
||||||
"sourceIsXButPackageFromYPrompt": "Az alkalmazás forrása „{}”, de a kiadási csomag innen származik: „{}”. Folytatja?",
|
"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",
|
||||||
"updatesAvailable": "Frissítések érhetők el",
|
"noNewUpdates": "Nincsenek új frissítések.",
|
||||||
"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",
|
"xHasAnUpdate": "A(z) {} frissítést kapott.",
|
||||||
"noNewUpdates": "Nincsenek új frissítések.",
|
"appsUpdated": "Alkalmazások frissítve",
|
||||||
"xHasAnUpdate": "A(z) {} frissítést kapott.",
|
"appsUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy/több app frissítése megtörtént a háttérben",
|
||||||
"appsUpdated": "Alkalmazások frissítve",
|
"xWasUpdatedToY": "{} frissítve a következőre: {}.",
|
||||||
"appsUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy/több app frissítése megtörtént a háttérben",
|
"errorCheckingUpdates": "Hiba a frissítések keresésekor",
|
||||||
"xWasUpdatedToY": "{} frissítve a következőre: {}.",
|
"errorCheckingUpdatesNotifDescription": "Értesítés, amely akkor jelenik meg, ha a háttérbeli frissítések ellenőrzése sikertelen",
|
||||||
"errorCheckingUpdates": "Hiba a frissítések keresésekor",
|
"appsRemoved": "Alkalmazások eltávolítva",
|
||||||
"errorCheckingUpdatesNotifDescription": "Értesítés, amely akkor jelenik meg, ha a háttérbeli frissítések ellenőrzése sikertelen",
|
"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",
|
||||||
"appsRemoved": "Alkalmazások eltávolítva",
|
"xWasRemovedDueToErrorY": "A(z) {} a következő hiba miatt lett 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",
|
"completeAppInstallation": "Teljes app telepítés",
|
||||||
"xWasRemovedDueToErrorY": "A(z) {} a következő hiba miatt lett eltávolítva: {}",
|
"obtainiumMustBeOpenToInstallApps": "Az Obtainiumnak megnyitva kell lennie az alkalmazások telepítéséhez",
|
||||||
"completeAppInstallation": "Teljes app telepítés",
|
"completeAppInstallationNotifDescription": "Megkéri a felhasználót, hogy térjen vissza az Obtainiumhoz, hogy befejezze az alkalmazás telepítését",
|
||||||
"obtainiumMustBeOpenToInstallApps": "Az Obtainiumnak megnyitva kell lennie az alkalmazások telepítéséhez",
|
"checkingForUpdates": "Frissítések keresése",
|
||||||
"completeAppInstallationNotifDescription": "Megkéri a felhasználót, hogy térjen vissza az Obtainiumhoz, hogy befejezze az alkalmazás telepítését",
|
"checkingForUpdatesNotifDescription": "Átmeneti értesítés, amely a frissítések keresésekor jelenik meg",
|
||||||
"checkingForUpdates": "Frissítések keresése",
|
"pleaseAllowInstallPerm": "Kérjük, engedélyezze az Obtainiumnak az alkalmazások telepítését",
|
||||||
"checkingForUpdatesNotifDescription": "Átmeneti értesítés, amely a frissítések keresésekor jelenik meg",
|
"trackOnly": "Csak követés",
|
||||||
"pleaseAllowInstallPerm": "Kérjük, engedélyezze az Obtainiumnak az alkalmazások telepítését",
|
"errorWithHttpStatusCode": "Hiba {}",
|
||||||
"trackOnly": "Csak követés",
|
"versionCorrectionDisabled": "Verzió korrekció letiltva (úgy tűnik, a beépülő modul nem működik)",
|
||||||
"errorWithHttpStatusCode": "Hiba {}",
|
"unknown": "Ismeretlen",
|
||||||
"versionCorrectionDisabled": "Verzió korrekció letiltva (úgy tűnik, a beépülő modul nem működik)",
|
"none": "Egyik sem",
|
||||||
"unknown": "Ismeretlen",
|
"never": "Soha",
|
||||||
"none": "Egyik sem",
|
"latestVersionX": "Legújabb verzió: {}",
|
||||||
"never": "Soha",
|
"installedVersionX": "Telepített verzió: {}",
|
||||||
"latestVersionX": "Legújabb verzió: {}",
|
"lastUpdateCheckX": "Frissítés ellenőrizve: {}",
|
||||||
"installedVersionX": "Telepített verzió: {}",
|
"remove": "Eltávolítás",
|
||||||
"lastUpdateCheckX": "Frissítés ellenőrizve: {}",
|
"yesMarkUpdated": "Igen, megjelölés frissítettként",
|
||||||
"remove": "Eltávolítás",
|
"fdroid": "F-Droid hivatalos",
|
||||||
"yesMarkUpdated": "Igen, megjelölés frissítettként",
|
"appIdOrName": "App ID vagy név",
|
||||||
"fdroid": "F-Droid Official",
|
"appId": "Alkalmazásazonosító",
|
||||||
"appIdOrName": "App ID vagy név",
|
"appWithIdOrNameNotFound": "Nem található app ezzel az azonosítóval vagy névvel",
|
||||||
"appId": "App ID",
|
"reposHaveMultipleApps": "A repók több alkalmazást is tartalmazhatnak",
|
||||||
"appWithIdOrNameNotFound": "Nem található app ezzel az azonosítóval vagy névvel",
|
"fdroidThirdPartyRepo": "F-Droid Harmadik-fél Repo",
|
||||||
"reposHaveMultipleApps": "A repók több alkalmazást is tartalmazhatnak",
|
"steamMobile": "Steam mobil",
|
||||||
"fdroidThirdPartyRepo": "F-Droid Harmadik-fél Repo",
|
"steamChat": "Steam Chat",
|
||||||
"steam": "Steam",
|
"install": "Telepít",
|
||||||
"steamMobile": "Steam Mobile",
|
"markInstalled": "Telepítettnek jelöl",
|
||||||
"steamChat": "Steam Chat",
|
"update": "Frissít",
|
||||||
"install": "Telepít",
|
"markUpdated": "Frissítettnek jelöl",
|
||||||
"markInstalled": "Telepítettnek jelöl",
|
"additionalOptions": "További lehetőségek",
|
||||||
"update": "Frissít",
|
"disableVersionDetection": "Verzió érzékelés letiltása",
|
||||||
"markUpdated": "Frissítettnek jelöl",
|
"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.",
|
||||||
"additionalOptions": "További lehetőségek",
|
"downloadingX": "{} letöltés",
|
||||||
"disableVersionDetection": "Verzió érzékelés letiltása",
|
"downloadNotifDescription": "Értesíti a felhasználót az app letöltésének előrehaladásáról",
|
||||||
"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.",
|
"noAPKFound": "Nem található APK",
|
||||||
"downloadingX": "{} letöltés",
|
"noVersionDetection": "Nincs verzió érzékelés",
|
||||||
"downloadNotifDescription": "Értesíti a felhasználót az app letöltésének előrehaladásáról",
|
"categorize": "Kategorizálás",
|
||||||
"noAPKFound": "Nem található APK",
|
"categories": "Kategóriák",
|
||||||
"noVersionDetection": "Nincs verzió érzékelés",
|
"category": "Kategória",
|
||||||
"categorize": "Kategorizálás",
|
"noCategory": "Nincs kategória",
|
||||||
"categories": "Kategóriák",
|
"noCategories": "Nincsenek kategóriák",
|
||||||
"category": "Kategória",
|
"deleteCategoriesQuestion": "Törli a kategóriákat?",
|
||||||
"noCategory": "Nincs kategória",
|
"categoryDeleteWarning": "A(z) {} összes app kategorizálatlan állapotba kerül.",
|
||||||
"deleteCategoryQuestion": "Törli a kategóriát?",
|
"addCategory": "Új kategória",
|
||||||
"categoryDeleteWarning": "A(z) {} összes app kategorizálatlan állapotba kerül.",
|
"label": "Címke",
|
||||||
"addCategory": "Új kategória",
|
"language": "Nyelv",
|
||||||
"label": "Címke",
|
"copiedToClipboard": "Másolva a vágólapra",
|
||||||
"language": "Nyelv",
|
"storagePermissionDenied": "Tárhely engedély megtagadva",
|
||||||
"copiedToClipboard": "Másolva a vágólapra",
|
"selectedCategorizeWarning": "Ez felváltja a kiválasztott alkalmazások meglévő kategória-beállításait.",
|
||||||
"storagePermissionDenied": "Tárhely engedély megtagadva",
|
"filterAPKsByRegEx": "Az APK-k szűrése reguláris kifejezéssel",
|
||||||
"selectedCategorizeWarning": "Ez felváltja a kiválasztott alkalmazások meglévő kategória-beállításait.",
|
"removeFromObtainium": "Eltávolítás az Obtainiumból",
|
||||||
"filterAPKsByRegEx": "Az APK-k szűrése reguláris kifejezéssel",
|
"uninstallFromDevice": "Eltávolítás a készülékről",
|
||||||
"removeFromObtainium": "Eltávolítás az Obtainiumból",
|
"onlyWorksWithNonVersionDetectApps": "Csak azoknál az alkalmazásoknál működik, amelyeknél a verzióérzékelés le van tiltva.",
|
||||||
"uninstallFromDevice": "Eltávolítás a készülékről",
|
"releaseDateAsVersion": "Használja a Kiadás dátumát, mint verziót",
|
||||||
"onlyWorksWithNonVersionDetectApps": "Csak azoknál az alkalmazásoknál működik, amelyeknél a verzióérzékelés le van tiltva.",
|
"releaseDateAsVersionExplanation": "Ezt a beállítást csak olyan alkalmazásoknál szabad használni, ahol a verzió érzékelése nem működik megfelelően, de elérhető a kiadás dátuma.",
|
||||||
"releaseDateAsVersion": "Használja a Kiadás dátumát, mint verziót",
|
"changes": "Változtatások",
|
||||||
"releaseDateAsVersionExplanation": "Ezt a beállítást csak olyan alkalmazásoknál szabad használni, ahol a verzió érzékelése nem működik megfelelően, de elérhető a kiadás dátuma.",
|
"releaseDate": "Kiadás dátuma",
|
||||||
"changes": "Változtatások",
|
"importFromURLsInFile": "Importálás fájlban található URL-ből (mint pl. OPML)",
|
||||||
"releaseDate": "Kiadás dátuma",
|
"versionDetectionExplanation": "A verzió karakterlánc egyeztetése az OS által észlelt verzióval",
|
||||||
"importFromURLsInFile": "Importálás fájlban található URL-ből (mint pl. OPML)",
|
"versionDetection": "Verzió érzékelés",
|
||||||
"versionDetection": "Verzió érzékelés",
|
"standardVersionDetection": "Alapért. verzió érzékelés",
|
||||||
"standardVersionDetection": "Alapért. verzió érzékelés",
|
"groupByCategory": "Csoportosítás Kategória alapján",
|
||||||
"groupByCategory": "Csoportosítás Kategória alapján",
|
"autoApkFilterByArch": "Ha lehetséges, próbálja CPU architektúra szerint szűrni az APK-kat",
|
||||||
"autoApkFilterByArch": "Ha lehetséges, próbálja CPU architektúra szerint szűrni az APK-okat",
|
"overrideSource": "Forrás felülbírálása",
|
||||||
"overrideSource": "Forrás felülbírálása",
|
"dontShowAgain": "Ne mutassa ezt újra",
|
||||||
"dontShowAgain": "Ne mutassa ezt újra",
|
"dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést",
|
||||||
"dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést",
|
"dontShowAPKOriginWarnings": "Ne jelenítsen meg az APK eredetére vonatkozó figyelmeztetéseket",
|
||||||
"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",
|
||||||
"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)",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Engedélyezi a Keresést és jobb APK felfedezés)",
|
"about": "Rólunk",
|
||||||
"about": "Rólunk",
|
"requiresCredentialsInSettings": "{}: Ehhez további hitelesítő adatokra van szükség (a Beállításokban)",
|
||||||
"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",
|
||||||
"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",
|
||||||
"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",
|
||||||
"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",
|
||||||
"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",
|
||||||
"checkUpdateOnDetailPage": "Frissítések keresése az app részleteit tartalmazó oldal megnyitásakor",
|
"disablePageTransitions": "Lap áttűnési animációk letiltása",
|
||||||
"disablePageTransitions": "Lap áttűnési animációk letiltása",
|
"reversePageTransitions": "Fordított lap áttűnési animációk",
|
||||||
"reversePageTransitions": "Fordított lap áttűnési animációk",
|
"minStarCount": "Minimális csillag szám",
|
||||||
"minStarCount": "Minimális csillag szám",
|
"addInfoBelow": "Adja hozzá ezt az infót alább.",
|
||||||
"addInfoBelow": "Adja hozzá ezt az infót alább.",
|
"addInfoInSettings": "Adja hozzá ezt az infót a Beállításokban.",
|
||||||
"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.",
|
||||||
"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.",
|
||||||
"gitlabSourceNote": "Előfordulhat, hogy a GitLab APK kibontása nem működik API-kulcs nélkül.",
|
"sortByLastLinkSegment": "Rendezés csak a link utolsó szegmense szerint",
|
||||||
"sortByFileNamesNotLinks": "Fájlnevek szerinti elrendezés teljes linkek helyett",
|
"filterReleaseNotesByRegEx": "Kiadási megjegyzések szűrése reguláris kifejezéssel",
|
||||||
"filterReleaseNotesByRegEx": "Kiadási megjegyzések szűrése reguláris kifejezéssel",
|
"customLinkFilterRegex": "Egyéni APK hivatkozásszűrő reguláris kifejezéssel (Alapérték '.apk$')",
|
||||||
"customLinkFilterRegex": "Egyéni APK hivatkozásszűrő reguláris kifejezéssel (Alapérték '.apk$')",
|
"appsPossiblyUpdated": "App frissítési kísérlet",
|
||||||
"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",
|
||||||
"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 {}.",
|
||||||
"xWasPossiblyUpdatedToY": "{} frissítve lehet erre {}.",
|
"enableBackgroundUpdates": "Frissítések a háttérben",
|
||||||
"backgroundUpdateReqsExplanation": "Előfordulhat, hogy nem minden appnál lehetséges a háttérbeli frissítés.",
|
"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.",
|
"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",
|
"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)",
|
"intermediateLinkRegex": "Szűrés egy 'köztes' látogatási linkre",
|
||||||
"bgUpdatesOnWiFiOnly": "Tiltsa le a háttérben frissítéseket, ha nincs Wi-Fi-n",
|
"filterByLinkText": "A hivatkozások szűrése linkszöveg alapján",
|
||||||
"autoSelectHighestVersionCode": "A legmagasabb verziószámú APK auto. kiválasztása",
|
"intermediateLinkNotFound": "Köztes link nem található",
|
||||||
"versionExtractionRegEx": "Verzió kibontása reguláris kifejezéssel",
|
"intermediateLink": "Köztes link",
|
||||||
"matchGroupToUse": "Párosítsa a csoportot a használathoz",
|
"exemptFromBackgroundUpdates": "Mentes a háttérben történő frissítések alól (ha engedélyezett)",
|
||||||
"highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat",
|
"bgUpdatesOnWiFiOnly": "Tiltsa le a háttérben frissítéseket, ha nincs Wi-Fi-n",
|
||||||
"pickExportDir": "Válassza az Exportálási könyvtárat",
|
"autoSelectHighestVersionCode": "A legmagasabb verziószámú APK auto. kiválasztása",
|
||||||
"autoExportOnChanges": "Auto-exportálás a változások után",
|
"versionExtractionRegEx": "Verzió kibontása reguláris kifejezéssel",
|
||||||
"filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel",
|
"matchGroupToUse": "Párosítsa a csoportot a használathoz",
|
||||||
"trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t",
|
"highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat",
|
||||||
"dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése",
|
"pickExportDir": "Válassza az Exportálási könyvtárat",
|
||||||
"reverseSort": "Fordított rendezés",
|
"autoExportOnChanges": "Auto-exportálás a változások után",
|
||||||
"debugMenu": "Hibakereső menü",
|
"includeSettings": "Tartalmazza a beállításokat",
|
||||||
"bgTaskStarted": "A háttérfeladat elindult – ellenőrizze a naplókat.",
|
"filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel",
|
||||||
"enableBackgroundUpdates": "Frissítések a háttérben",
|
"trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t",
|
||||||
"runBgCheckNow": "Futtassa a Háttérben frissítés ellenőrzését most",
|
"dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése",
|
||||||
"removeAppQuestion": {
|
"reverseSort": "Fordított rendezés",
|
||||||
"one": "Eltávolítja az alkalmazást?",
|
"takeFirstLink": "Vegye az első linket",
|
||||||
"other": "Eltávolítja az alkalmazást?"
|
"skipSort": "A válogatás kihagyása",
|
||||||
},
|
"debugMenu": "Hibakereső menü",
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"bgTaskStarted": "A háttérfeladat elindult – ellenőrizze a naplókat.",
|
||||||
"one": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva",
|
"runBgCheckNow": "Futtassa a Háttérben frissítés ellenőrzését most",
|
||||||
"other": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva"
|
"versionExtractWholePage": "Alkalmazza a Version Extraction Regex-et az egész oldalra",
|
||||||
},
|
"installing": "Telepítés",
|
||||||
"bgUpdateGotErrorRetryInMinutes": {
|
"skipUpdateNotifications": "A frissítési értesítések kihagyása",
|
||||||
"one": "A háttérfrissítések ellenőrzése {}-t észlelt, {} perc múlva ütemezi az újrapróbálkozást",
|
"updatesAvailableNotifChannel": "Frissítések érhetők el",
|
||||||
"other": "A háttérfrissítések ellenőrzése {}-t észlelt, {} perc múlva ütemezi az újrapróbálkozást"
|
"appsUpdatedNotifChannel": "Alkalmazások frissítve",
|
||||||
},
|
"appsPossiblyUpdatedNotifChannel": "App frissítési kísérlet",
|
||||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
"errorCheckingUpdatesNotifChannel": "Hiba a frissítések keresésekor",
|
||||||
"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",
|
"appsRemovedNotifChannel": "Alkalmazások eltávolítva",
|
||||||
"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"
|
"downloadingXNotifChannel": "{} letöltés",
|
||||||
},
|
"completeAppInstallationNotifChannel": "Teljes app telepítés",
|
||||||
"apps": {
|
"checkingForUpdatesNotifChannel": "Frissítések keresése",
|
||||||
"one": "{} app",
|
"onlyCheckInstalledOrTrackOnlyApps": "Csak a telepített és a csak követhető appokat ellenőrizze frissítésekért",
|
||||||
"other": "{} app"
|
"supportFixedAPKURL": "Támogatja a rögzített APK URL-eket",
|
||||||
},
|
"selectX": "Kiválaszt {}",
|
||||||
"url": {
|
"parallelDownloads": "Párhuzamos letöltéseket enged",
|
||||||
"one": "{} URL",
|
"installMethod": "Telepítési mód",
|
||||||
"other": "{} URL"
|
"normal": "Normál",
|
||||||
},
|
"root": "Gyökér",
|
||||||
"minute": {
|
"shizukuBinderNotFound": "A Shizuku nem fut",
|
||||||
"one": "{} perc",
|
"useSystemFont": "Használja a rendszer betűtípusát",
|
||||||
"other": "{} perc"
|
"systemFontError": "Hiba a rendszer betűtípusának betöltésekor: {}",
|
||||||
},
|
"useVersionCodeAsOSVersion": "Az app versionCode használata a rendszer által észlelt verzióként",
|
||||||
"hour": {
|
"requestHeader": "Kérelem fejléc",
|
||||||
"one": "{} óra",
|
"useLatestAssetDateAsReleaseDate": "Használja a legújabb tartalomfeltöltést megjelenési dátumként",
|
||||||
"other": "{} óra"
|
"defaultPseudoVersioningMethod": "Alapértelmezett álversziós módszer",
|
||||||
},
|
"partialAPKHash": "Részleges APK Hash",
|
||||||
"day": {
|
"APKLinkHash": "APK Link Hash",
|
||||||
"one": "{} nap",
|
"directAPKLink": "Közvetlen APK Link",
|
||||||
"other": "{} nap"
|
"pseudoVersionInUse": "Egy álverzió van használatban",
|
||||||
},
|
"installed": "Telepített",
|
||||||
"clearedNLogsBeforeXAfterY": {
|
"latest": "Legújabb",
|
||||||
"one": "{n} napló törölve (előtte = {előtte}, utána = {utána})",
|
"invertRegEx": "Invertált reguláris kifejezés",
|
||||||
"other": "{n} napló törölve (előtte = {előtte}, utána = {utána})"
|
"note": "Note",
|
||||||
},
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"xAndNMoreUpdatesAvailable": {
|
"removeAppQuestion": {
|
||||||
"one": "A(z) {} és 1 további alkalmazás frissítéseket kapott.",
|
"one": "Eltávolítja az alkalmazást?",
|
||||||
"other": "{} és {} további alkalmazás frissítéseket kapott."
|
"other": "Eltávolítja az alkalmazást?"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesInstalled": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "A(z) {} és 1 további alkalmazás frissítve.",
|
"one": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva",
|
||||||
"other": "{} és {} további alkalmazás frissítve."
|
"other": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"bgUpdateGotErrorRetryInMinutes": {
|
||||||
"one": "{} és 1 további alkalmazás is frissült.",
|
"one": "A háttérfrissítések ellenőrzése {}-t észlelt, {} perc múlva ütemezi az újrapróbálkozást",
|
||||||
"other": "{} és {} további alkalmazás is frissült."
|
"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."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"unexpectedError": "Errore imprevisto",
|
"unexpectedError": "Errore imprevisto",
|
||||||
"ok": "Va bene",
|
"ok": "Va bene",
|
||||||
"and": "e",
|
"and": "e",
|
||||||
"githubPATLabel": "GitHub Personal Access Token (diminuisce limite di traffico)",
|
"githubPATLabel": "GitHub Personal Access Token (aumenta limite di traffico)",
|
||||||
"includePrereleases": "Includi prerelease",
|
"includePrereleases": "Includi prerelease",
|
||||||
"fallbackToOlderReleases": "Ripiega su release precedenti",
|
"fallbackToOlderReleases": "Ripiega su release precedenti",
|
||||||
"filterReleaseTitlesByRegEx": "Filtra release con espressioni regolari",
|
"filterReleaseTitlesByRegEx": "Filtra release con espressioni regolari",
|
||||||
@@ -51,28 +51,29 @@
|
|||||||
"percentProgress": "Avanzamento: {}%",
|
"percentProgress": "Avanzamento: {}%",
|
||||||
"pleaseWait": "In attesa",
|
"pleaseWait": "In attesa",
|
||||||
"updateAvailable": "Aggiornamento disponibile",
|
"updateAvailable": "Aggiornamento disponibile",
|
||||||
"estimateInBracketsShort": "(prev.)",
|
|
||||||
"notInstalled": "Non installato",
|
"notInstalled": "Non installato",
|
||||||
"estimateInBrackets": "(previsto)",
|
"pseudoVersion": "pseudo-versione",
|
||||||
"selectAll": "Seleziona tutto",
|
"selectAll": "Seleziona tutto",
|
||||||
"deselectN": "Deseleziona {}",
|
"deselectX": "Deseleziona {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "Verà effettuata la rimozione di {}, ma non la disinstallazione.",
|
"xWillBeRemovedButRemainInstalled": "Verà effettuata la rimozione di {}, ma non la disinstallazione.",
|
||||||
"removeSelectedAppsQuestion": "Rimuovere le app selezionate?",
|
"removeSelectedAppsQuestion": "Rimuovere le app selezionate?",
|
||||||
"removeSelectedApps": "Rimuovi le app selezionate",
|
"removeSelectedApps": "Rimuovi le app selezionate",
|
||||||
"updateX": "Aggiorna {}",
|
"updateX": "Aggiorna {}",
|
||||||
"installX": "Installa {}",
|
"installX": "Installa {}",
|
||||||
"markXTrackOnlyAsUpdated": "Contrassegna {}\n(Solo-Monitoraggio)\ncome aggiornato",
|
"markXTrackOnlyAsUpdated": "Contrassegna {}\n(Solo-Monitoraggio)\ncome aggiornata",
|
||||||
"changeX": "Modifica {}",
|
"changeX": "Modifica {}",
|
||||||
"installUpdateApps": "Installa/Aggiorna app",
|
"installUpdateApps": "Installa/Aggiorna app",
|
||||||
"installUpdateSelectedApps": "Installa/Aggiorna le app selezionate",
|
"installUpdateSelectedApps": "Installa/Aggiorna le app selezionate",
|
||||||
"markXSelectedAppsAsUpdated": "Contrassegnare le {} app selezionate come aggiornate?",
|
"markXSelectedAppsAsUpdated": "Contrassegnare le {} app selezionate come aggiornate?",
|
||||||
"no": "No",
|
"no": "NO",
|
||||||
"yes": "Sì",
|
"yes": "Sì",
|
||||||
"markSelectedAppsUpdated": "Contrassegna le app selezionate come aggiornate",
|
"markSelectedAppsUpdated": "Contrassegna le app selezionate come aggiornate",
|
||||||
"pinToTop": "Fissa in alto",
|
"pinToTop": "Fissa in alto",
|
||||||
"unpinFromTop": "Rimuovi dall'alto",
|
"unpinFromTop": "Rimuovi dall'alto",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "Ripristinare lo stato d'installazione delle app selezionate?",
|
"resetInstallStatusForSelectedAppsQuestion": "Ripristinare lo stato d'installazione delle app selezionate?",
|
||||||
"installStatusOfXWillBeResetExplanation": "Lo stato d'installazione di ogni app selezionata sarà ripristinato.\n\nCiò può essere d'aiuto nel caso in cui la versione mostrata dell'app in Obtainium non sia corretta a causa di un aggiornamento fallito o di altri problemi.",
|
"installStatusOfXWillBeResetExplanation": "Lo stato d'installazione di ogni app selezionata sarà ripristinato.\n\nCiò può essere d'aiuto nel caso in cui la versione mostrata dell'app in Obtainium non sia corretta a causa di un aggiornamento fallito o di altri problemi.",
|
||||||
|
"customLinkMessage": "Questi collegamenti funzionano sui dispositivi con Obtainium installato",
|
||||||
|
"shareAppConfigLinks": "Condividi la configurazione dell'app come collegamento HTML",
|
||||||
"shareSelectedAppURLs": "Condividi gli URL delle app selezionate",
|
"shareSelectedAppURLs": "Condividi gli URL delle app selezionate",
|
||||||
"resetInstallStatus": "Ripristina lo stato d'installazione",
|
"resetInstallStatus": "Ripristina lo stato d'installazione",
|
||||||
"more": "Altro",
|
"more": "Altro",
|
||||||
@@ -103,7 +104,6 @@
|
|||||||
"importErrors": "Errori di importazione",
|
"importErrors": "Errori di importazione",
|
||||||
"importedXOfYApps": "{} app di {} importate.",
|
"importedXOfYApps": "{} app di {} importate.",
|
||||||
"followingURLsHadErrors": "I seguenti URL contengono errori:",
|
"followingURLsHadErrors": "I seguenti URL contengono errori:",
|
||||||
"okay": "Va bene",
|
|
||||||
"selectURL": "Seleziona l'URL",
|
"selectURL": "Seleziona l'URL",
|
||||||
"selectURLs": "Seleziona gli URL",
|
"selectURLs": "Seleziona gli URL",
|
||||||
"pick": "Seleziona",
|
"pick": "Seleziona",
|
||||||
@@ -111,8 +111,6 @@
|
|||||||
"dark": "Scuro",
|
"dark": "Scuro",
|
||||||
"light": "Chiaro",
|
"light": "Chiaro",
|
||||||
"followSystem": "Segui il sistema",
|
"followSystem": "Segui il sistema",
|
||||||
"obtainium": "Obtainium",
|
|
||||||
"materialYou": "Material You",
|
|
||||||
"useBlackTheme": "Usa il tema Nero puro",
|
"useBlackTheme": "Usa il tema Nero puro",
|
||||||
"appSortBy": "App ordinate per",
|
"appSortBy": "App ordinate per",
|
||||||
"authorName": "Autore/Nome",
|
"authorName": "Autore/Nome",
|
||||||
@@ -128,7 +126,7 @@
|
|||||||
"pinUpdates": "Fissa aggiornamenti disponibili in alto",
|
"pinUpdates": "Fissa aggiornamenti disponibili in alto",
|
||||||
"updates": "Aggiornamenti",
|
"updates": "Aggiornamenti",
|
||||||
"sourceSpecific": "Specifiche per la fonte",
|
"sourceSpecific": "Specifiche per la fonte",
|
||||||
"appSource": "Sorgente dell'app",
|
"appSource": "Codice dell'app",
|
||||||
"noLogs": "Nessun log",
|
"noLogs": "Nessun log",
|
||||||
"appLogs": "Log dell'app",
|
"appLogs": "Log dell'app",
|
||||||
"close": "Chiudi",
|
"close": "Chiudi",
|
||||||
@@ -169,20 +167,19 @@
|
|||||||
"installedVersionX": "Versione installata: {}",
|
"installedVersionX": "Versione installata: {}",
|
||||||
"lastUpdateCheckX": "Ultimo controllo degli aggiornamenti: {}",
|
"lastUpdateCheckX": "Ultimo controllo degli aggiornamenti: {}",
|
||||||
"remove": "Rimuovi",
|
"remove": "Rimuovi",
|
||||||
"yesMarkUpdated": "Sì, contrassegna come aggiornato",
|
"yesMarkUpdated": "Sì, contrassegna come aggiornata",
|
||||||
"fdroid": "F-Droid ufficiale",
|
"fdroid": "F-Droid ufficiale",
|
||||||
"appIdOrName": "ID o nome dell'app",
|
"appIdOrName": "ID o nome dell'app",
|
||||||
"appId": "ID dell'app",
|
"appId": "ID dell'app",
|
||||||
"appWithIdOrNameNotFound": "Non è stata trovata alcuna app con quell'ID o nome",
|
"appWithIdOrNameNotFound": "Non è stata trovata alcuna app con quell'ID o nome",
|
||||||
"reposHaveMultipleApps": "I repository possono contenere più app",
|
"reposHaveMultipleApps": "I repository possono contenere più app",
|
||||||
"fdroidThirdPartyRepo": "Repository F-Droid di terze parti",
|
"fdroidThirdPartyRepo": "Repository F-Droid di terze parti",
|
||||||
"steam": "Steam",
|
"steamMobile": "Mobile a vapore",
|
||||||
"steamMobile": "Steam Mobile",
|
"steamChat": "Chat di vapore",
|
||||||
"steamChat": "Steam Chat",
|
|
||||||
"install": "Installa",
|
"install": "Installa",
|
||||||
"markInstalled": "Contrassegna come installato",
|
"markInstalled": "Contrassegna come installata",
|
||||||
"update": "Aggiorna",
|
"update": "Aggiorna",
|
||||||
"markUpdated": "Contrassegna come aggiornato",
|
"markUpdated": "Contrassegna come aggiornata",
|
||||||
"additionalOptions": "Opzioni aggiuntive",
|
"additionalOptions": "Opzioni aggiuntive",
|
||||||
"disableVersionDetection": "Disattiva il rilevamento della versione",
|
"disableVersionDetection": "Disattiva il rilevamento della versione",
|
||||||
"noVersionDetectionExplanation": "Questa opzione dovrebbe essere usata solo per le app la cui versione non viene rilevata correttamente.",
|
"noVersionDetectionExplanation": "Questa opzione dovrebbe essere usata solo per le app la cui versione non viene rilevata correttamente.",
|
||||||
@@ -212,6 +209,7 @@
|
|||||||
"changes": "Novità",
|
"changes": "Novità",
|
||||||
"releaseDate": "Data di rilascio",
|
"releaseDate": "Data di rilascio",
|
||||||
"importFromURLsInFile": "Importa da URL in file (come OPML)",
|
"importFromURLsInFile": "Importa da URL in file (come OPML)",
|
||||||
|
"versionDetectionExplanation": "Riconcilia la stringa della versione con la versione rilevata dal sistema operativo",
|
||||||
"versionDetection": "Rilevamento di versione",
|
"versionDetection": "Rilevamento di versione",
|
||||||
"standardVersionDetection": "Rilevamento di versione standard",
|
"standardVersionDetection": "Rilevamento di versione standard",
|
||||||
"groupByCategory": "Raggruppa per categoria",
|
"groupByCategory": "Raggruppa per categoria",
|
||||||
@@ -221,45 +219,87 @@
|
|||||||
"dontShowTrackOnlyWarnings": "Non mostrare gli avvisi 'Solo-Monitoraggio'",
|
"dontShowTrackOnlyWarnings": "Non mostrare gli avvisi 'Solo-Monitoraggio'",
|
||||||
"dontShowAPKOriginWarnings": "Non mostrare gli avvisi di origine dell'APK",
|
"dontShowAPKOriginWarnings": "Non mostrare gli avvisi di origine dell'APK",
|
||||||
"moveNonInstalledAppsToBottom": "Sposta le app non installate in fondo alla lista",
|
"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",
|
"about": "Informazioni",
|
||||||
"requiresCredentialsInSettings": "Servono credenziali aggiuntive (in Impostazioni)",
|
"requiresCredentialsInSettings": "{}: Servono credenziali aggiuntive (in Impostazioni)",
|
||||||
"checkOnStart": "Controlla una volta all'avvio",
|
"checkOnStart": "Controlla una volta all'avvio",
|
||||||
"tryInferAppIdFromCode": "Prova a dedurre l'ID dell'app dal codice sorgente",
|
"tryInferAppIdFromCode": "Prova a dedurre l'ID dell'app dal codice sorgente",
|
||||||
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
"removeOnExternalUninstall": "Rimuovi automaticamente app disinstallate esternamente",
|
||||||
"pickHighestVersionCode": "Auto-select highest version code APK",
|
"pickHighestVersionCode": "Auto-seleziona APK con version code più alto",
|
||||||
"checkUpdateOnDetailPage": "Check for updates on opening an App detail page",
|
"checkUpdateOnDetailPage": "Controlla aggiornamenti all'apertura dei dettagli dell'app",
|
||||||
"disablePageTransitions": "Disable page transition animations",
|
"disablePageTransitions": "Disattiva animazioni di transizione pagina",
|
||||||
"reversePageTransitions": "Reverse page transition animations",
|
"reversePageTransitions": "Inverti animazioni di transizione pagina",
|
||||||
"minStarCount": "Minimum Star Count",
|
"minStarCount": "Numero minimo di stelle",
|
||||||
"addInfoBelow": "Add this info below.",
|
"addInfoBelow": "Aggiungi questa info sotto.",
|
||||||
"addInfoInSettings": "Add this info in the Settings.",
|
"addInfoInSettings": "Aggiungi questa info nelle impostazioni.",
|
||||||
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
|
"githubSourceNote": "Il limite di ricerca GitHub può essere evitato usando una chiave API.",
|
||||||
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
|
"gitlabSourceNote": "L'estrazione di APK da GitLab potrebbe non funzionare senza chiave API.",
|
||||||
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
|
"sortByLastLinkSegment": "Ordina solo in base all'ultimo segmento del collegamento",
|
||||||
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
|
"filterReleaseNotesByRegEx": "Filtra le note di rilascio con espressione regolare",
|
||||||
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
|
"customLinkFilterRegex": "Filtra link APK personalizzato con espressione regolare (predefinito '.apk$')",
|
||||||
"appsPossiblyUpdated": "App Updates Attempted",
|
"appsPossiblyUpdated": "Aggiornamenti app tentati",
|
||||||
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
|
"appsPossiblyUpdatedNotifDescription": "Notifica all'utente che sono stati potenzialmente applicati in secondo piano aggiornamenti a una o più app",
|
||||||
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
|
"xWasPossiblyUpdatedToY": "{} potrebbe essere stata aggiornata alla {}.",
|
||||||
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
|
"enableBackgroundUpdates": "Attiva aggiornamenti in secondo piano",
|
||||||
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
|
"backgroundUpdateReqsExplanation": "Gli aggiornamenti in secondo piano potrebbero non essere possibili per tutte le app.",
|
||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"backgroundUpdateLimitsExplanation": "La riuscita di un'installazione in secondo piano può essere determinata solo quando viene aperto Obtainium.",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"verifyLatestTag": "Verifica l'etichetta 'Latest'",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"intermediateLinkRegex": "Filtro per un collegamento 'intermedio' da visitare",
|
||||||
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
"filterByLinkText": "Filtra i collegamenti in base al testo del collegamento",
|
||||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
"intermediateLinkNotFound": "Link intermedio non trovato",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"intermediateLink": "Collegamento intermedio",
|
||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"exemptFromBackgroundUpdates": "Esente da aggiornamenti in secondo piano (se attivo)",
|
||||||
"pickExportDir": "Pick Export Directory",
|
"bgUpdatesOnWiFiOnly": "Disattiva aggiornamenti in secondo piano quando non si usa il WiFi",
|
||||||
"autoExportOnChanges": "Auto-export on changes",
|
"autoSelectHighestVersionCode": "Auto-seleziona APK con versionCode più alto",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"versionExtractionRegEx": "RegEx di estrazione versione",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"matchGroupToUse": "Gruppo da usare",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"highlightTouchTargets": "Evidenzia elementi toccabili meno ovvi",
|
||||||
"reverseSort": "Reverse sorting",
|
"pickExportDir": "Scegli cartella esp.",
|
||||||
"debugMenu": "Debug Menu",
|
"autoExportOnChanges": "Auto-esporta dopo modifiche",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"includeSettings": "Includi impostazioni",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"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",
|
||||||
|
"takeFirstLink": "Prendi il primo link",
|
||||||
|
"skipSort": "Salta ordinamento",
|
||||||
|
"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",
|
||||||
|
"supportFixedAPKURL": "Supporta URL fissi di APK",
|
||||||
|
"selectX": "Seleziona {}",
|
||||||
|
"parallelDownloads": "Permetti download paralleli",
|
||||||
|
"installMethod": "Metodo d'installazione",
|
||||||
|
"normal": "Normale",
|
||||||
|
"root": "Radice",
|
||||||
|
"shizukuBinderNotFound": "Shizuku non è in esecuzione",
|
||||||
|
"useSystemFont": "Utilizza il carattere di sistema",
|
||||||
|
"systemFontError": "Errore durante il caricamento del carattere di sistema: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Utilizza il codice versione dell'app come versione rilevata dal sistema operativo",
|
||||||
|
"requestHeader": "Intestazione della richiesta",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Utilizza l'ultimo caricamento della risorsa come data di rilascio",
|
||||||
|
"defaultPseudoVersioningMethod": "Metodo di pseudoversione predefinito",
|
||||||
|
"partialAPKHash": "Hash APK parziale",
|
||||||
|
"APKLinkHash": "Hash collegamento APK",
|
||||||
|
"directAPKLink": "Collegamento APK diretto",
|
||||||
|
"pseudoVersionInUse": "È in uso una pseudo-versione",
|
||||||
|
"installed": "Installato",
|
||||||
|
"latest": "Ultimo",
|
||||||
|
"invertRegEx": "Inverti espressione regolare",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Rimuovere l'app?",
|
"one": "Rimuovere l'app?",
|
||||||
"other": "Rimuovere le app?"
|
"other": "Rimuovere le app?"
|
||||||
@@ -309,7 +349,7 @@
|
|||||||
"other": "{} e altre {} app sono state aggiornate."
|
"other": "{} e altre {} app sono state aggiornate."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
"one": "{} and 1 more app may have been updated.",
|
"one": "{} e un'altra app potrebbero essere state aggiornate.",
|
||||||
"other": "{} and {} more apps may have been updated."
|
"other": "{} e altre {} app potrebbero essere state aggiornate."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,11 +51,10 @@
|
|||||||
"percentProgress": "ダウンロード中: {}%",
|
"percentProgress": "ダウンロード中: {}%",
|
||||||
"pleaseWait": "しばらくお待ちください",
|
"pleaseWait": "しばらくお待ちください",
|
||||||
"updateAvailable": "アップデートが利用可能",
|
"updateAvailable": "アップデートが利用可能",
|
||||||
"estimateInBracketsShort": "(推定)",
|
|
||||||
"notInstalled": "未インストール",
|
"notInstalled": "未インストール",
|
||||||
"estimateInBrackets": "(推定)",
|
"pseudoVersion": "疑似バージョン",
|
||||||
"selectAll": "すべて選択",
|
"selectAll": "すべて選択",
|
||||||
"deselectN": "{}件の選択を解除",
|
"deselectX": "{}件の選択を解除",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} はObtainiumから削除されますが、デバイスにはインストールされたままです。",
|
"xWillBeRemovedButRemainInstalled": "{} はObtainiumから削除されますが、デバイスにはインストールされたままです。",
|
||||||
"removeSelectedAppsQuestion": "選択したアプリを削除しますか?",
|
"removeSelectedAppsQuestion": "選択したアプリを削除しますか?",
|
||||||
"removeSelectedApps": "選択したアプリを削除する",
|
"removeSelectedApps": "選択したアプリを削除する",
|
||||||
@@ -73,6 +72,8 @@
|
|||||||
"unpinFromTop": "トップから固定解除",
|
"unpinFromTop": "トップから固定解除",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "選択したアプリのインストール状態をリセットしますか?",
|
"resetInstallStatusForSelectedAppsQuestion": "選択したアプリのインストール状態をリセットしますか?",
|
||||||
"installStatusOfXWillBeResetExplanation": "選択したアプリのインストール状態がリセットされます。\n\nアップデートに失敗した場合など、Obtainiumに表示されるアプリのバージョンが正しくない場合に有効です。",
|
"installStatusOfXWillBeResetExplanation": "選択したアプリのインストール状態がリセットされます。\n\nアップデートに失敗した場合など、Obtainiumに表示されるアプリのバージョンが正しくない場合に有効です。",
|
||||||
|
"customLinkMessage": "これらのリンクはObtainiumがインストールされたデバイスで動作します",
|
||||||
|
"shareAppConfigLinks": "アプリの設定をHTMLリンクとして共有する",
|
||||||
"shareSelectedAppURLs": "選択したアプリのURLを共有する",
|
"shareSelectedAppURLs": "選択したアプリのURLを共有する",
|
||||||
"resetInstallStatus": "インストール状態をリセットする",
|
"resetInstallStatus": "インストール状態をリセットする",
|
||||||
"more": "もっと見る",
|
"more": "もっと見る",
|
||||||
@@ -103,7 +104,6 @@
|
|||||||
"importErrors": "インポートエラー",
|
"importErrors": "インポートエラー",
|
||||||
"importedXOfYApps": "{} / {} アプリをインポートしました",
|
"importedXOfYApps": "{} / {} アプリをインポートしました",
|
||||||
"followingURLsHadErrors": "以下のURLでエラーが発生しました:",
|
"followingURLsHadErrors": "以下のURLでエラーが発生しました:",
|
||||||
"okay": "OK",
|
|
||||||
"selectURL": "URLを選択",
|
"selectURL": "URLを選択",
|
||||||
"selectURLs": "URLを選択",
|
"selectURLs": "URLを選択",
|
||||||
"pick": "選択",
|
"pick": "選択",
|
||||||
@@ -111,8 +111,6 @@
|
|||||||
"dark": "ダーク",
|
"dark": "ダーク",
|
||||||
"light": "ライト",
|
"light": "ライト",
|
||||||
"followSystem": "システムに従う",
|
"followSystem": "システムに従う",
|
||||||
"obtainium": "Obtainium",
|
|
||||||
"materialYou": "Material You",
|
|
||||||
"useBlackTheme": "ピュアブラックダークテーマを使用する",
|
"useBlackTheme": "ピュアブラックダークテーマを使用する",
|
||||||
"appSortBy": "アプリの並び方",
|
"appSortBy": "アプリの並び方",
|
||||||
"authorName": "作者名/アプリ名",
|
"authorName": "作者名/アプリ名",
|
||||||
@@ -154,8 +152,8 @@
|
|||||||
"appsRemovedNotifDescription": "アプリの読み込み中にエラーが発生したため、1つまたは複数のアプリが削除されたことをユーザーに通知する",
|
"appsRemovedNotifDescription": "アプリの読み込み中にエラーが発生したため、1つまたは複数のアプリが削除されたことをユーザーに通知する",
|
||||||
"xWasRemovedDueToErrorY": "このエラーのため、{} は削除されました: {}",
|
"xWasRemovedDueToErrorY": "このエラーのため、{} は削除されました: {}",
|
||||||
"completeAppInstallation": "アプリのインストールを完了する",
|
"completeAppInstallation": "アプリのインストールを完了する",
|
||||||
"obtainiumMustBeOpenToInstallApps": "アプリをインストールするにはObtainiumを開いている必要があります。",
|
"obtainiumMustBeOpenToInstallApps": "アプリをインストールするにはObtainiumが開いている必要があります",
|
||||||
"completeAppInstallationNotifDescription": "アプリのインストールを完了するために、Obtainiumに戻る必要があります。",
|
"completeAppInstallationNotifDescription": "アプリのインストールを完了するために、Obtainiumに戻る必要があります",
|
||||||
"checkingForUpdates": "アップデートを確認中",
|
"checkingForUpdates": "アップデートを確認中",
|
||||||
"checkingForUpdatesNotifDescription": "アップデートを確認する際に表示される一時的な通知",
|
"checkingForUpdatesNotifDescription": "アップデートを確認する際に表示される一時的な通知",
|
||||||
"pleaseAllowInstallPerm": "Obtainiumによるアプリのインストールを許可してください。",
|
"pleaseAllowInstallPerm": "Obtainiumによるアプリのインストールを許可してください。",
|
||||||
@@ -170,15 +168,14 @@
|
|||||||
"lastUpdateCheckX": "最終アップデート確認: {}",
|
"lastUpdateCheckX": "最終アップデート確認: {}",
|
||||||
"remove": "削除",
|
"remove": "削除",
|
||||||
"yesMarkUpdated": "はい、アップデート済みとしてマークします",
|
"yesMarkUpdated": "はい、アップデート済みとしてマークします",
|
||||||
"fdroid": "F-Droid Official",
|
"fdroid": "F-Droid公式",
|
||||||
"appIdOrName": "アプリのIDまたは名前",
|
"appIdOrName": "アプリのIDまたは名前",
|
||||||
"appId": "App ID",
|
"appId": "アプリID",
|
||||||
"appWithIdOrNameNotFound": "そのIDや名前を持つアプリは見つかりませんでした",
|
"appWithIdOrNameNotFound": "そのIDや名前を持つアプリは見つかりませんでした",
|
||||||
"reposHaveMultipleApps": "リポジトリには複数のアプリが含まれることがあります",
|
"reposHaveMultipleApps": "リポジトリには複数のアプリが含まれることがあります",
|
||||||
"fdroidThirdPartyRepo": "F-Droid サードパーティリポジトリ",
|
"fdroidThirdPartyRepo": "F-Droid サードパーティリポジトリ",
|
||||||
"steam": "Steam",
|
"steamMobile": "Steamモバイル",
|
||||||
"steamMobile": "Steam Mobile",
|
"steamChat": "Steamチャット",
|
||||||
"steamChat": "Steam Chat",
|
|
||||||
"install": "インストール",
|
"install": "インストール",
|
||||||
"markInstalled": "インストール済みとしてマークする",
|
"markInstalled": "インストール済みとしてマークする",
|
||||||
"update": "アップデート",
|
"update": "アップデート",
|
||||||
@@ -212,10 +209,11 @@
|
|||||||
"changes": "変更点",
|
"changes": "変更点",
|
||||||
"releaseDate": "リリース日",
|
"releaseDate": "リリース日",
|
||||||
"importFromURLsInFile": "ファイル(OPMLなど)内のURLからインポート",
|
"importFromURLsInFile": "ファイル(OPMLなど)内のURLからインポート",
|
||||||
|
"versionDetectionExplanation": "バージョン文字列とOSから検出されたバージョンを照合する",
|
||||||
"versionDetection": "バージョン検出",
|
"versionDetection": "バージョン検出",
|
||||||
"standardVersionDetection": "標準のバージョン検出",
|
"standardVersionDetection": "標準のバージョン検出",
|
||||||
"groupByCategory": "カテゴリ別にグループ化する",
|
"groupByCategory": "カテゴリ別にグループ化する",
|
||||||
"autoApkFilterByArch": "可能であれば,CPUアーキテクチャによるAPKのフィルタリングを試みる",
|
"autoApkFilterByArch": "可能であれば、CPUアーキテクチャによるAPKのフィルタリングを試みる",
|
||||||
"overrideSource": "ソースの上書き",
|
"overrideSource": "ソースの上書き",
|
||||||
"dontShowAgain": "二度と表示しない",
|
"dontShowAgain": "二度と表示しない",
|
||||||
"dontShowTrackOnlyWarnings": "「追跡のみ」の警告を表示しない",
|
"dontShowTrackOnlyWarnings": "「追跡のみ」の警告を表示しない",
|
||||||
@@ -223,7 +221,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "未インストールのアプリをアプリ一覧の下部に移動させる",
|
"moveNonInstalledAppsToBottom": "未インストールのアプリをアプリ一覧の下部に移動させる",
|
||||||
"gitlabPATLabel": "GitLab パーソナルアクセストークン\n(検索とより良いAPK検出の有効化)",
|
"gitlabPATLabel": "GitLab パーソナルアクセストークン\n(検索とより良いAPK検出の有効化)",
|
||||||
"about": "概要",
|
"about": "概要",
|
||||||
"requiresCredentialsInSettings": "これには追加の認証が必要です (設定にて)",
|
"requiresCredentialsInSettings": "{}: これには追加の認証が必要です (設定にて)",
|
||||||
"checkOnStart": "起動時にアップデートを確認する",
|
"checkOnStart": "起動時にアップデートを確認する",
|
||||||
"tryInferAppIdFromCode": "ソースコードからApp IDを推測する",
|
"tryInferAppIdFromCode": "ソースコードからApp IDを推測する",
|
||||||
"removeOnExternalUninstall": "外部でアンインストールされたアプリを自動的に削除する",
|
"removeOnExternalUninstall": "外部でアンインストールされたアプリを自動的に削除する",
|
||||||
@@ -236,7 +234,7 @@
|
|||||||
"addInfoInSettings": "設定でこの情報を追加してください。",
|
"addInfoInSettings": "設定でこの情報を追加してください。",
|
||||||
"githubSourceNote": "GitHubのレート制限はAPIキーを使うことで回避できます。",
|
"githubSourceNote": "GitHubのレート制限はAPIキーを使うことで回避できます。",
|
||||||
"gitlabSourceNote": "GitLabのAPK抽出はAPIキーがないと動作しない場合があります。",
|
"gitlabSourceNote": "GitLabのAPK抽出はAPIキーがないと動作しない場合があります。",
|
||||||
"sortByFileNamesNotLinks": "フルのリンクではなくファイル名でソートする",
|
"sortByLastLinkSegment": "リンクの最後のセグメントのみでソートする",
|
||||||
"filterReleaseNotesByRegEx": "正規表現でリリースノートをフィルタリングする",
|
"filterReleaseNotesByRegEx": "正規表現でリリースノートをフィルタリングする",
|
||||||
"customLinkFilterRegex": "正規表現によるカスタムリンクフィルター (デフォルト '.apk$')",
|
"customLinkFilterRegex": "正規表現によるカスタムリンクフィルター (デフォルト '.apk$')",
|
||||||
"appsPossiblyUpdated": "アプリのアップデートを試行",
|
"appsPossiblyUpdated": "アプリのアップデートを試行",
|
||||||
@@ -246,23 +244,62 @@
|
|||||||
"backgroundUpdateReqsExplanation": "バックグラウンドアップデートは、すべてのアプリで可能とは限りません。",
|
"backgroundUpdateReqsExplanation": "バックグラウンドアップデートは、すべてのアプリで可能とは限りません。",
|
||||||
"backgroundUpdateLimitsExplanation": "バックグラウンドアップデートが成功したかどうかは、Obtainiumを起動したときにしか判断できません。",
|
"backgroundUpdateLimitsExplanation": "バックグラウンドアップデートが成功したかどうかは、Obtainiumを起動したときにしか判断できません。",
|
||||||
"verifyLatestTag": "'latest'タグを確認する",
|
"verifyLatestTag": "'latest'タグを確認する",
|
||||||
"intermediateLinkRegex": "最初にアクセスする「中間」リンクをフィルタリングする",
|
"intermediateLinkRegex": "訪問する「中間」リンクのフィルター",
|
||||||
|
"filterByLinkText": "テキストでリンクをフィルタリングする",
|
||||||
"intermediateLinkNotFound": "中間リンクが見つかりませんでした",
|
"intermediateLinkNotFound": "中間リンクが見つかりませんでした",
|
||||||
|
"intermediateLink": "中間リンク",
|
||||||
"exemptFromBackgroundUpdates": "バックグラウンドアップデートを行わない (有効な場合)",
|
"exemptFromBackgroundUpdates": "バックグラウンドアップデートを行わない (有効な場合)",
|
||||||
"bgUpdatesOnWiFiOnly": "WiFiを使用していない場合,バックグラウンドアップデートを無効にする",
|
"bgUpdatesOnWiFiOnly": "WiFiを使用していない場合、バックグラウンドアップデートを無効にする",
|
||||||
"autoSelectHighestVersionCode": "最も高いバージョンコードのAPKを自動で選択する",
|
"autoSelectHighestVersionCode": "最も高いバージョンコードのAPKを自動で選択する",
|
||||||
"versionExtractionRegEx": "バージョン抽出の正規表現",
|
"versionExtractionRegEx": "バージョン抽出の正規表現",
|
||||||
"matchGroupToUse": "使用するマッチしたグループ",
|
"matchGroupToUse": "使用するマッチしたグループ",
|
||||||
"highlightTouchTargets": "目立たないタップ可能な対象をハイライトする",
|
"highlightTouchTargets": "目立たないタップ可能な対象をハイライトする",
|
||||||
"pickExportDir": "エクスポートディレクトリを選択",
|
"pickExportDir": "エクスポートディレクトリを選択",
|
||||||
"autoExportOnChanges": "変更があった際に自動でエクスポートする",
|
"autoExportOnChanges": "変更があった際に自動でエクスポートする",
|
||||||
|
"includeSettings": "設定を含む",
|
||||||
"filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする",
|
"filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする",
|
||||||
"trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
|
"trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "APIからのリリース順を保持する",
|
||||||
"reverseSort": "Reverse sorting",
|
"reverseSort": "逆順ソート",
|
||||||
"debugMenu": "Debug Menu",
|
"takeFirstLink": "最初のリンクを取得する",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"skipSort": "ソートをスキップする",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"debugMenu": "デバッグメニュー",
|
||||||
|
"bgTaskStarted": "バックグラウンドタスクが開始されました - ログを確認してください。",
|
||||||
|
"runBgCheckNow": "今すぐバックグラウンドでのアップデート確認を開始する",
|
||||||
|
"versionExtractWholePage": "バージョン抽出の正規表現をページ全体に適用する",
|
||||||
|
"installing": "インストール中",
|
||||||
|
"skipUpdateNotifications": "アップデート通知を行わない",
|
||||||
|
"updatesAvailableNotifChannel": "アップデートが利用可能",
|
||||||
|
"appsUpdatedNotifChannel": "アプリをアップデートしました",
|
||||||
|
"appsPossiblyUpdatedNotifChannel": "アプリのアップデートを試行",
|
||||||
|
"errorCheckingUpdatesNotifChannel": "アップデート確認中のエラー",
|
||||||
|
"appsRemovedNotifChannel": "削除されたアプリ",
|
||||||
|
"downloadingXNotifChannel": "{} をダウンロード中",
|
||||||
|
"completeAppInstallationNotifChannel": "アプリのインストールを完了する",
|
||||||
|
"checkingForUpdatesNotifChannel": "アップデートを確認中",
|
||||||
|
"onlyCheckInstalledOrTrackOnlyApps": "インストール済みのアプリと「追跡のみ」のアプリのアップデートのみを確認する",
|
||||||
|
"supportFixedAPKURL": "固定されたAPKのURLをサポートする",
|
||||||
|
"selectX": "{} 選択",
|
||||||
|
"parallelDownloads": "並行ダウンロードを許可する",
|
||||||
|
"installMethod": "インストール方法",
|
||||||
|
"normal": "通常",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizukuが起動していません",
|
||||||
|
"useSystemFont": "システムフォントを使用する",
|
||||||
|
"systemFontError": "システムフォントの読み込みエラー: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "アプリのバージョンコードをOSで検出されたバージョンとして使用する",
|
||||||
|
"requestHeader": "リクエストヘッダー",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "最新のアセットアップロードをリリース日として使用する",
|
||||||
|
"defaultPseudoVersioningMethod": "デフォルトの疑似バージョン管理方法",
|
||||||
|
"partialAPKHash": "部分的なAPKのハッシュ",
|
||||||
|
"APKLinkHash": "APKリンクのハッシュ",
|
||||||
|
"directAPKLink": "APKのダイレクトリンク",
|
||||||
|
"pseudoVersionInUse": "疑似バージョンが使用されています",
|
||||||
|
"installed": "インストール済み",
|
||||||
|
"latest": "最新",
|
||||||
|
"invertRegEx": "正規表現を反転",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "アプリを削除しますか?",
|
"one": "アプリを削除しますか?",
|
||||||
"other": "アプリを削除しますか?"
|
"other": "アプリを削除しますか?"
|
||||||
|
|||||||
355
assets/translations/nl.json
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
{
|
||||||
|
"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\n(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-repo's met ster",
|
||||||
|
"uname": "Gebruikersnaam",
|
||||||
|
"wrongArgNum": "Onjuist aantal argumenten verstrekt.",
|
||||||
|
"xIsTrackOnly": "{} is alleen tracken",
|
||||||
|
"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": "(Alleen track)",
|
||||||
|
"searchableInBrackets": "(Doorzoekbaar)",
|
||||||
|
"appsString": "Apps",
|
||||||
|
"noApps": "Geen Apps",
|
||||||
|
"noAppsForFilter": "Geen Apps voor filter",
|
||||||
|
"byX": "Door {}",
|
||||||
|
"percentProgress": "Vooruitgang: {}%",
|
||||||
|
"pleaseWait": "Even geduld",
|
||||||
|
"updateAvailable": "Update beschikbaar",
|
||||||
|
"notInstalled": "Niet geinstalleerd",
|
||||||
|
"pseudoVersion": "pseudo-versie",
|
||||||
|
"selectAll": "Selecteer alles",
|
||||||
|
"deselectX": "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.",
|
||||||
|
"customLinkMessage": "Deze links werken op apparaten waarop Obtainium is geïnstalleerd",
|
||||||
|
"shareAppConfigLinks": "App-configuratie delen als HTML-link",
|
||||||
|
"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": "Filteren *",
|
||||||
|
"filterApps": "Filter apps",
|
||||||
|
"appName": "App naam",
|
||||||
|
"author": "Auteur",
|
||||||
|
"upToDateApps": "Up-to-date apps",
|
||||||
|
"nonInstalledApps": "Niet-geïnstalleerde apps",
|
||||||
|
"importExport": "Importeren/Exporteren",
|
||||||
|
"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": "Importeer {}",
|
||||||
|
"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:",
|
||||||
|
"selectURL": "Selecteer URL",
|
||||||
|
"selectURLs": "Selecteer URL's",
|
||||||
|
"pick": "Kies",
|
||||||
|
"theme": "Thema",
|
||||||
|
"dark": "Donker",
|
||||||
|
"light": "Licht",
|
||||||
|
"followSystem": "Volg systeem",
|
||||||
|
"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": "Alleen track",
|
||||||
|
"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-ambtenaar",
|
||||||
|
"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 Repository van derden",
|
||||||
|
"steamMobile": "Stoommobiel",
|
||||||
|
"steamChat": "Steamchat",
|
||||||
|
"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": "Etiket",
|
||||||
|
"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)",
|
||||||
|
"versionDetectionExplanation": "Versiereeks afstemmen met versie gedetecteerd door besturingssysteem",
|
||||||
|
"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": "Laat dit niet meer zien",
|
||||||
|
"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.",
|
||||||
|
"sortByLastLinkSegment": "Sorteren op alleen het laatste segment van de link",
|
||||||
|
"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": "Het succes van een installatie in de achtergrond kan alleen worden bepaald wanneer Obtainium is geopend.",
|
||||||
|
"verifyLatestTag": "Verifieer de 'Laatste'-tag",
|
||||||
|
"intermediateLinkRegex": "Filter voor een 'Intermediaire' link om te bezoeken",
|
||||||
|
"filterByLinkText": "Links filteren op linktekst",
|
||||||
|
"intermediateLinkNotFound": "Tussenliggende link niet gevonden",
|
||||||
|
"intermediateLink": "Intermediaire link",
|
||||||
|
"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",
|
||||||
|
"includeSettings": "Instellingen opnemen",
|
||||||
|
"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",
|
||||||
|
"takeFirstLink": "Neem de eerste link",
|
||||||
|
"skipSort": "Sorteren overslaan",
|
||||||
|
"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",
|
||||||
|
"supportFixedAPKURL": "Ondersteuning vaste APK URL's",
|
||||||
|
"selectX": "Selecteer {}",
|
||||||
|
"parallelDownloads": "Parallelle downloads toestaan",
|
||||||
|
"installMethod": "Installatiemethode",
|
||||||
|
"normal": "Normaal",
|
||||||
|
"root": "Wortel",
|
||||||
|
"shizukuBinderNotFound": "Shizuku draait niet",
|
||||||
|
"useSystemFont": "Gebruik het systeemlettertype",
|
||||||
|
"systemFontError": "Fout bij het laden van het systeemlettertype: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Gebruik app versieCode als door OS gedetecteerde versie",
|
||||||
|
"requestHeader": "Verzoekkoptekst",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Gebruik laatste upload als releasedatum",
|
||||||
|
"defaultPseudoVersioningMethod": "Standaard pseudo-versiebeheermethode",
|
||||||
|
"partialAPKHash": "Gedeeltelijke APK-hash",
|
||||||
|
"APKLinkHash": "APK-link-hash",
|
||||||
|
"directAPKLink": "Directe APK-link",
|
||||||
|
"pseudoVersionInUse": "Er is een pseudo-versie in gebruik",
|
||||||
|
"installed": "Geïnstalleerd",
|
||||||
|
"latest": "Laatste",
|
||||||
|
"invertRegEx": "Reguliere expressie omkeren",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
|
"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": "{} URL's"
|
||||||
|
},
|
||||||
|
"minute": {
|
||||||
|
"one": "{} minuut",
|
||||||
|
"other": "{} minuten"
|
||||||
|
},
|
||||||
|
"hour": {
|
||||||
|
"one": "{} uur",
|
||||||
|
"other": "{} uur"
|
||||||
|
},
|
||||||
|
"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."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,4 @@
|
|||||||
{
|
{
|
||||||
"noDescription": "Brak opisu",
|
|
||||||
"no": "Nie",
|
|
||||||
"okay": "Okej",
|
|
||||||
"appId": "ID aplikacji",
|
|
||||||
"bgUpdateGotErrorRetryInMinutes": {
|
|
||||||
"one": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minutę",
|
|
||||||
"few": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minuty",
|
|
||||||
"many": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minut",
|
|
||||||
"other": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minuty"
|
|
||||||
},
|
|
||||||
"invalidURLForSource": "Nieprawidłowy adres URL aplikacji {}",
|
"invalidURLForSource": "Nieprawidłowy adres URL aplikacji {}",
|
||||||
"noReleaseFound": "Nie można znaleźć odpowiedniego wydania",
|
"noReleaseFound": "Nie można znaleźć odpowiedniego wydania",
|
||||||
"noVersionFound": "Nie można określić wersji wydania",
|
"noVersionFound": "Nie można określić wersji wydania",
|
||||||
@@ -16,7 +6,7 @@
|
|||||||
"cantInstallOlderVersion": "Nie można zainstalować starszej wersji aplikacji",
|
"cantInstallOlderVersion": "Nie można zainstalować starszej wersji aplikacji",
|
||||||
"appIdMismatch": "Pobrane ID pakietu nie pasuje do istniejącego ID aplikacji",
|
"appIdMismatch": "Pobrane ID pakietu nie pasuje do istniejącego ID aplikacji",
|
||||||
"functionNotImplemented": "Ta klasa nie zaimplementowała tej funkcji",
|
"functionNotImplemented": "Ta klasa nie zaimplementowała tej funkcji",
|
||||||
"placeholder": "Placeholder",
|
"placeholder": "Zbiornik",
|
||||||
"someErrors": "Wystąpiły pewne błędy",
|
"someErrors": "Wystąpiły pewne błędy",
|
||||||
"unexpectedError": "Nieoczekiwany błąd",
|
"unexpectedError": "Nieoczekiwany błąd",
|
||||||
"ok": "Okej",
|
"ok": "Okej",
|
||||||
@@ -26,6 +16,7 @@
|
|||||||
"fallbackToOlderReleases": "Powracaj do starszych wersji",
|
"fallbackToOlderReleases": "Powracaj do starszych wersji",
|
||||||
"filterReleaseTitlesByRegEx": "Filtruj tytuły wydań wg. wyrażeń regularnych",
|
"filterReleaseTitlesByRegEx": "Filtruj tytuły wydań wg. wyrażeń regularnych",
|
||||||
"invalidRegEx": "Nieprawidłowe wyrażenie regularne",
|
"invalidRegEx": "Nieprawidłowe wyrażenie regularne",
|
||||||
|
"noDescription": "Brak opisu",
|
||||||
"cancel": "Anuluj",
|
"cancel": "Anuluj",
|
||||||
"continue": "Kontynuuj",
|
"continue": "Kontynuuj",
|
||||||
"requiredInBrackets": "(Wymagane)",
|
"requiredInBrackets": "(Wymagane)",
|
||||||
@@ -34,11 +25,11 @@
|
|||||||
"githubStarredRepos": "Repozytoria GitHub oznaczone gwiazdką",
|
"githubStarredRepos": "Repozytoria GitHub oznaczone gwiazdką",
|
||||||
"uname": "Nazwa użytkownika",
|
"uname": "Nazwa użytkownika",
|
||||||
"wrongArgNum": "Nieprawidłowa liczba podanych argumentów",
|
"wrongArgNum": "Nieprawidłowa liczba podanych argumentów",
|
||||||
"xIsTrackOnly": "{} jest tylko obserwowana",
|
"xIsTrackOnly": "{} jest tylko obserwowane",
|
||||||
"source": "Źródło",
|
"source": "Źródło",
|
||||||
"app": "Aplikacja",
|
"app": "Aplikacja",
|
||||||
"appsFromSourceAreTrackOnly": "Aplikacje z tego źródła są „Obserwowane”.",
|
"appsFromSourceAreTrackOnly": "Aplikacje z tego źródła są tylko obserwowane.",
|
||||||
"youPickedTrackOnly": "Wybrano opcję „Tylko obserwuj”.",
|
"youPickedTrackOnly": "Wybrano opcję \"Tylko obserwuj\".",
|
||||||
"trackOnlyAppDescription": "Aplikacja będzie obserwowana pod kątem aktualizacji, ale Obtainium nie będzie w stanie jej pobrać ani zainstalować.",
|
"trackOnlyAppDescription": "Aplikacja będzie obserwowana pod kątem aktualizacji, ale Obtainium nie będzie w stanie jej pobrać ani zainstalować.",
|
||||||
"cancelled": "Anulowano",
|
"cancelled": "Anulowano",
|
||||||
"appAlreadyAdded": "Aplikacja już została dodana",
|
"appAlreadyAdded": "Aplikacja już została dodana",
|
||||||
@@ -52,7 +43,7 @@
|
|||||||
"additionalOptsFor": "Dodatkowe opcje dla {}",
|
"additionalOptsFor": "Dodatkowe opcje dla {}",
|
||||||
"supportedSources": "Obsługiwane źródła",
|
"supportedSources": "Obsługiwane źródła",
|
||||||
"trackOnlyInBrackets": "(tylko obserwowane)",
|
"trackOnlyInBrackets": "(tylko obserwowane)",
|
||||||
"searchableInBrackets": "(Wyszukiwalne)",
|
"searchableInBrackets": "(wyszukiwalne)",
|
||||||
"appsString": "Aplikacje",
|
"appsString": "Aplikacje",
|
||||||
"noApps": "Brak aplikacji",
|
"noApps": "Brak aplikacji",
|
||||||
"noAppsForFilter": "Brak aplikacji dla filtra",
|
"noAppsForFilter": "Brak aplikacji dla filtra",
|
||||||
@@ -60,27 +51,29 @@
|
|||||||
"percentProgress": "Postęp: {}%",
|
"percentProgress": "Postęp: {}%",
|
||||||
"pleaseWait": "Proszę czekać",
|
"pleaseWait": "Proszę czekać",
|
||||||
"updateAvailable": "Dostępna aktualizacja",
|
"updateAvailable": "Dostępna aktualizacja",
|
||||||
"estimateInBracketsShort": "(Szac.)",
|
|
||||||
"notInstalled": "Nie zainstalowano",
|
"notInstalled": "Nie zainstalowano",
|
||||||
"estimateInBrackets": "(Szacunkowo)",
|
"pseudoVersion": "pseudowersja",
|
||||||
"selectAll": "Zaznacz wszystkie",
|
"selectAll": "Zaznacz wszystkie",
|
||||||
"deselectN": "Odznacz {}",
|
"deselectX": "Odznacz {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} zostanie usunięty z Obtainium, ale pozostanie zainstalowany na urządzeniu.",
|
"xWillBeRemovedButRemainInstalled": "{} zostanie usunięty z Obtainium, ale pozostanie zainstalowany na urządzeniu.",
|
||||||
"removeSelectedAppsQuestion": "Usunąć wybrane aplikacje?",
|
"removeSelectedAppsQuestion": "Usunąć wybrane aplikacje?",
|
||||||
"removeSelectedApps": "Usuń wybrane aplikacje",
|
"removeSelectedApps": "Usuń wybrane aplikacje",
|
||||||
"updateX": "Zaktualizuj {}",
|
"updateX": "Zaktualizuj {}",
|
||||||
"installX": "Zainstaluj {}",
|
"installX": "Zainstaluj {}",
|
||||||
"markXTrackOnlyAsUpdated": "Oznacz {}\n(Tylko obserwowana)\njako zaktualizowaną",
|
"markXTrackOnlyAsUpdated": "Oznacz {}\n(tylko obserwowana)\njako zaktualizowaną",
|
||||||
"changeX": "Zmień {}",
|
"changeX": "Zmień {}",
|
||||||
"installUpdateApps": "Instaluj/aktualizuj aplikacje",
|
"installUpdateApps": "Instaluj/aktualizuj aplikacje",
|
||||||
"installUpdateSelectedApps": "Zainstaluj/zaktualizuj wybrane aplikacje",
|
"installUpdateSelectedApps": "Zainstaluj/zaktualizuj wybrane aplikacje",
|
||||||
"markXSelectedAppsAsUpdated": "Oznaczyć {} wybranych aplikacji jako zaktualizowane?",
|
"markXSelectedAppsAsUpdated": "Oznaczyć {} wybranych aplikacji jako zaktualizowane?",
|
||||||
|
"no": "Nie",
|
||||||
"yes": "Tak",
|
"yes": "Tak",
|
||||||
"markSelectedAppsUpdated": "Oznacz wybrane aplikacje jako zaktualizowane",
|
"markSelectedAppsUpdated": "Oznacz wybrane aplikacje jako zaktualizowane",
|
||||||
"pinToTop": "Przypnij",
|
"pinToTop": "Przypnij",
|
||||||
"unpinFromTop": "Odepnij",
|
"unpinFromTop": "Odepnij",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "Zresetować status instalacji dla wybranych aplikacji?",
|
"resetInstallStatusForSelectedAppsQuestion": "Zresetować status instalacji dla wybranych aplikacji?",
|
||||||
"installStatusOfXWillBeResetExplanation": "Stan instalacji wybranych aplikacji zostanie zresetowany.\n\nMoże być to pomocne, gdy wersja aplikacji wyświetlana w Obtainium jest nieprawidłowa z powodu nieudanych aktualizacji lub innych problemów.",
|
"installStatusOfXWillBeResetExplanation": "Stan instalacji wybranych aplikacji zostanie zresetowany.\n\nMoże być to pomocne, gdy wersja aplikacji wyświetlana w Obtainium jest nieprawidłowa z powodu nieudanych aktualizacji lub innych problemów.",
|
||||||
|
"customLinkMessage": "Te linki działają na urządzeniach z zainstalowanym Obtainium",
|
||||||
|
"shareAppConfigLinks": "Udostępnij konfigurację aplikacji jako link HTML",
|
||||||
"shareSelectedAppURLs": "Udostępnij wybrane adresy URL aplikacji",
|
"shareSelectedAppURLs": "Udostępnij wybrane adresy URL aplikacji",
|
||||||
"resetInstallStatus": "Zresetuj stan instalacji",
|
"resetInstallStatus": "Zresetuj stan instalacji",
|
||||||
"more": "Więcej",
|
"more": "Więcej",
|
||||||
@@ -107,7 +100,7 @@
|
|||||||
"searchX": "Przeszukaj {}",
|
"searchX": "Przeszukaj {}",
|
||||||
"noResults": "Nie znaleziono wyników",
|
"noResults": "Nie znaleziono wyników",
|
||||||
"importX": "Importuj {}",
|
"importX": "Importuj {}",
|
||||||
"importedAppsIdDisclaimer": "Zaimportowane aplikacje mogą być wyświetlane jako „Niezainstalowane”.\nAby to naprawić, zainstaluj je ponownie za pomocą Obtainium.\nNie powinno to mieć wpływu na dane aplikacji.\n\nDotyczy tylko adresów URL i metod importu innych aplikacji.",
|
"importedAppsIdDisclaimer": "Zaimportowane aplikacje mogą być wyświetlane jako niezainstalowane.\nAby to naprawić, przeinstaluj je za pomocą Obtainium.\nNie powinno to mieć wpływu na dane aplikacji.\n\nDotyczy tylko adresu URL i innych metod importu.",
|
||||||
"importErrors": "Błędy importowania",
|
"importErrors": "Błędy importowania",
|
||||||
"importedXOfYApps": "Zaimportowano {} z {} aplikacji.",
|
"importedXOfYApps": "Zaimportowano {} z {} aplikacji.",
|
||||||
"followingURLsHadErrors": "Następujące adresy URL zawierały błędy:",
|
"followingURLsHadErrors": "Następujące adresy URL zawierały błędy:",
|
||||||
@@ -118,8 +111,6 @@
|
|||||||
"dark": "Ciemny",
|
"dark": "Ciemny",
|
||||||
"light": "Jasny",
|
"light": "Jasny",
|
||||||
"followSystem": "Zgodny z systemem",
|
"followSystem": "Zgodny z systemem",
|
||||||
"obtainium": "Obtainium",
|
|
||||||
"materialYou": "Material You",
|
|
||||||
"useBlackTheme": "Użyj czarnego motywu",
|
"useBlackTheme": "Użyj czarnego motywu",
|
||||||
"appSortBy": "Sortuj aplikacje według",
|
"appSortBy": "Sortuj aplikacje według",
|
||||||
"authorName": "Autor/Nazwa",
|
"authorName": "Autor/Nazwa",
|
||||||
@@ -149,20 +140,20 @@
|
|||||||
"warning": "Uwaga",
|
"warning": "Uwaga",
|
||||||
"sourceIsXButPackageFromYPrompt": "Źródłem aplikacji jest '{}', ale pakiet wydania pochodzi z '{}'. Kontynuować?",
|
"sourceIsXButPackageFromYPrompt": "Źródłem aplikacji jest '{}', ale pakiet wydania pochodzi z '{}'. Kontynuować?",
|
||||||
"updatesAvailable": "Dostępne aktualizacje",
|
"updatesAvailable": "Dostępne aktualizacje",
|
||||||
"updatesAvailableNotifDescription": "Powiadamia użytkownika o dostępności aktualizacji dla jednej lub więcej aplikacji obserwowanych przez Obtainium",
|
"updatesAvailableNotifDescription": "Informuje o dostępności aktualizacji dla jednej lub więcej aplikacji obserwowanych przez Obtainium",
|
||||||
"noNewUpdates": "Brak nowych aktualizacji.",
|
"noNewUpdates": "Brak nowych aktualizacji.",
|
||||||
"xHasAnUpdate": "{} ma aktualizację.",
|
"xHasAnUpdate": "{} ma aktualizację.",
|
||||||
"appsUpdated": "Zaktualizowane aplikacje",
|
"appsUpdated": "Zaktualizowano aplikacje",
|
||||||
"appsUpdatedNotifDescription": "Powiadamia użytkownika, gdy jedna lub więcej aplikacji zostało zaktualizowanych w tle",
|
"appsUpdatedNotifDescription": "Informuje, gdy co najmniej jedna aplikacja została zaktualizowana w tle",
|
||||||
"xWasUpdatedToY": "{} zaktualizowano do {}.",
|
"xWasUpdatedToY": "{} zaktualizowano do {}.",
|
||||||
"errorCheckingUpdates": "Sprawdzanie błędów aktualizacji",
|
"errorCheckingUpdates": "Błąd sprawdzania aktualizacji",
|
||||||
"errorCheckingUpdatesNotifDescription": "Powiadomienie wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się",
|
"errorCheckingUpdatesNotifDescription": "Jest wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się",
|
||||||
"appsRemoved": "Usunięte aplikacje",
|
"appsRemoved": "Usunięte aplikacje",
|
||||||
"appsRemovedNotifDescription": "Powiadamia użytkownika, gdy jedna lub więcej aplikacji zostało usuniętych z powodu błędów wczytywania",
|
"appsRemovedNotifDescription": "Informuje, gdy co najmniej jedna aplikacja została usunięta z powodu błędów podczas wczytywania",
|
||||||
"xWasRemovedDueToErrorY": "Usunięto {} z powodu błędu: {}",
|
"xWasRemovedDueToErrorY": "Usunięto {} z powodu błędu: {}",
|
||||||
"completeAppInstallation": "Ukończenie instalacji aplikacji",
|
"completeAppInstallation": "Ukończenie instalacji aplikacji",
|
||||||
"obtainiumMustBeOpenToInstallApps": "Aby zainstalować aplikacje, Obtainium musi być otwarte",
|
"obtainiumMustBeOpenToInstallApps": "Aby zainstalować aplikacje, Obtainium musi być otwarte",
|
||||||
"completeAppInstallationNotifDescription": "Prosi użytkownika o powrót do Obtainium w celu dokończenia instalacji aplikacji",
|
"completeAppInstallationNotifDescription": "Informuje o możliwości powrotu do Obtainium w celu dokończenia instalacji aplikacji",
|
||||||
"checkingForUpdates": "Sprawdzanie aktualizacji",
|
"checkingForUpdates": "Sprawdzanie aktualizacji",
|
||||||
"checkingForUpdatesNotifDescription": "Tymczasowe powiadomienie pojawiające się podczas sprawdzania aktualizacji",
|
"checkingForUpdatesNotifDescription": "Tymczasowe powiadomienie pojawiające się podczas sprawdzania aktualizacji",
|
||||||
"pleaseAllowInstallPerm": "Pozwól Obtainium instalować aplikacje",
|
"pleaseAllowInstallPerm": "Pozwól Obtainium instalować aplikacje",
|
||||||
@@ -179,12 +170,12 @@
|
|||||||
"yesMarkUpdated": "Tak, oznacz jako zaktualizowane",
|
"yesMarkUpdated": "Tak, oznacz jako zaktualizowane",
|
||||||
"fdroid": "Oficjalny F-Droid",
|
"fdroid": "Oficjalny F-Droid",
|
||||||
"appIdOrName": "ID aplikacji lub nazwa",
|
"appIdOrName": "ID aplikacji lub nazwa",
|
||||||
|
"appId": "ID aplikacji",
|
||||||
"appWithIdOrNameNotFound": "Nie znaleziono aplikacji o tym identyfikatorze lub nazwie",
|
"appWithIdOrNameNotFound": "Nie znaleziono aplikacji o tym identyfikatorze lub nazwie",
|
||||||
"reposHaveMultipleApps": "Repozytoria mogą zawierać wiele aplikacji",
|
"reposHaveMultipleApps": "Repozytoria mogą zawierać wiele aplikacji",
|
||||||
"fdroidThirdPartyRepo": "Zewnętrzne repo F-Droid",
|
"fdroidThirdPartyRepo": "Zewnętrzne repo F-Droid",
|
||||||
"steam": "Steam",
|
|
||||||
"steamMobile": "Mobilny Steam",
|
"steamMobile": "Mobilny Steam",
|
||||||
"steamChat": "Steam Chat",
|
"steamChat": "Czat Steam",
|
||||||
"install": "Instaluj",
|
"install": "Instaluj",
|
||||||
"markInstalled": "Oznacz jako zainstalowane",
|
"markInstalled": "Oznacz jako zainstalowane",
|
||||||
"update": "Zaktualizuj",
|
"update": "Zaktualizuj",
|
||||||
@@ -193,7 +184,7 @@
|
|||||||
"disableVersionDetection": "Wyłącz wykrywanie wersji",
|
"disableVersionDetection": "Wyłącz wykrywanie wersji",
|
||||||
"noVersionDetectionExplanation": "Opcja ta powinna być używana tylko w przypadku aplikacji, w których wykrywanie wersji nie działa poprawnie.",
|
"noVersionDetectionExplanation": "Opcja ta powinna być używana tylko w przypadku aplikacji, w których wykrywanie wersji nie działa poprawnie.",
|
||||||
"downloadingX": "Pobieranie {}",
|
"downloadingX": "Pobieranie {}",
|
||||||
"downloadNotifDescription": "Powiadamia użytkownika o postępach w pobieraniu aplikacji",
|
"downloadNotifDescription": "Informuje o postępach w pobieraniu aplikacji",
|
||||||
"noAPKFound": "Nie znaleziono pakietu APK",
|
"noAPKFound": "Nie znaleziono pakietu APK",
|
||||||
"noVersionDetection": "Bez wykrywania wersji",
|
"noVersionDetection": "Bez wykrywania wersji",
|
||||||
"categorize": "Kategoryzuj",
|
"categorize": "Kategoryzuj",
|
||||||
@@ -218,18 +209,19 @@
|
|||||||
"changes": "Zmiany",
|
"changes": "Zmiany",
|
||||||
"releaseDate": "Data wydania",
|
"releaseDate": "Data wydania",
|
||||||
"importFromURLsInFile": "Importuj z adresów URL w pliku (typu OPML)",
|
"importFromURLsInFile": "Importuj z adresów URL w pliku (typu OPML)",
|
||||||
|
"versionDetectionExplanation": "Uzgodnij ciąg wersji z wersją wykrytą w systemie operacyjnym",
|
||||||
"versionDetection": "Wykrywanie wersji",
|
"versionDetection": "Wykrywanie wersji",
|
||||||
"standardVersionDetection": "Standardowe wykrywanie wersji",
|
"standardVersionDetection": "Standardowe wykrywanie wersji",
|
||||||
"groupByCategory": "Grupuj według kategorii",
|
"groupByCategory": "Grupuj według kategorii",
|
||||||
"autoApkFilterByArch": "Spróbuj filtrować pliki APK według architektury procesora, jeśli to możliwe",
|
"autoApkFilterByArch": "Spróbuj filtrować pliki APK według architektury procesora, jeśli to możliwe",
|
||||||
"overrideSource": "Nadpisz źródło",
|
"overrideSource": "Nadpisz źródło",
|
||||||
"dontShowAgain": "Nie pokazuj tego ponownie",
|
"dontShowAgain": "Nie pokazuj tego ponownie",
|
||||||
"dontShowTrackOnlyWarnings": "Nie pokazuj ostrzeżeń „Tylko obserwowana”",
|
"dontShowTrackOnlyWarnings": "Nie pokazuj ostrzeżeń \"Tylko obserwowana\"",
|
||||||
"dontShowAPKOriginWarnings": "Nie pokazuj ostrzeżeń o pochodzeniu APK",
|
"dontShowAPKOriginWarnings": "Nie pokazuj ostrzeżeń o pochodzeniu APK",
|
||||||
"moveNonInstalledAppsToBottom": "Przenieś niezainstalowane aplikacje na dół widoku aplikacji",
|
"moveNonInstalledAppsToBottom": "Przenieś niezainstalowane aplikacje na dół widoku aplikacji",
|
||||||
"gitlabPATLabel": "Osobisty token dostępu GitLab\n(Umożliwia wyszukiwanie i lepsze wykrywanie APK)",
|
"gitlabPATLabel": "Osobisty token dostępu GitLab\n(Umożliwia wyszukiwanie i lepsze wykrywanie APK)",
|
||||||
"about": "Więcej informacji",
|
"about": "Więcej informacji",
|
||||||
"requiresCredentialsInSettings": "Wymaga to dodatkowych poświadczeń (w Ustawieniach)",
|
"requiresCredentialsInSettings": "{}: Wymaga to dodatkowych poświadczeń (w Ustawieniach)",
|
||||||
"checkOnStart": "Sprawdź aktualizacje przy uruchomieniu",
|
"checkOnStart": "Sprawdź aktualizacje przy uruchomieniu",
|
||||||
"tryInferAppIdFromCode": "Spróbuj wywnioskować identyfikator aplikacji z kodu źródłowego",
|
"tryInferAppIdFromCode": "Spróbuj wywnioskować identyfikator aplikacji z kodu źródłowego",
|
||||||
"removeOnExternalUninstall": "Automatyczne usuń odinstalowane zewnętrznie aplikacje",
|
"removeOnExternalUninstall": "Automatyczne usuń odinstalowane zewnętrznie aplikacje",
|
||||||
@@ -242,30 +234,72 @@
|
|||||||
"addInfoInSettings": "Dodaj tę informację w Ustawieniach.",
|
"addInfoInSettings": "Dodaj tę informację w Ustawieniach.",
|
||||||
"githubSourceNote": "Limit żądań GitHub można ominąć za pomocą klucza API.",
|
"githubSourceNote": "Limit żądań GitHub można ominąć za pomocą klucza API.",
|
||||||
"gitlabSourceNote": "Pozyskiwanie pliku APK z GitLab może nie działać bez klucza API.",
|
"gitlabSourceNote": "Pozyskiwanie pliku APK z GitLab może nie działać bez klucza API.",
|
||||||
"sortByFileNamesNotLinks": "Sortuj wg nazw plików zamiast pełnych linków",
|
"sortByLastLinkSegment": "Sortuj tylko według ostatniego segmentu łącza",
|
||||||
"filterReleaseNotesByRegEx": "Filtruj informacje o wersji według wyrażenia regularnego",
|
"filterReleaseNotesByRegEx": "Filtruj informacje o wersji według wyrażenia regularnego",
|
||||||
"customLinkFilterRegex": "Filtruj linki APK według wyrażenia regularnego (domyślnie \".apk$\")",
|
"customLinkFilterRegex": "Filtruj linki APK według wyrażenia regularnego (domyślnie \".apk$\")",
|
||||||
"appsPossiblyUpdated": "Informuj o próbach aktualizacji",
|
"appsPossiblyUpdated": "Aplikacje mogły zostać zaktualizowane",
|
||||||
"appsPossiblyUpdatedNotifDescription": "Powiadamiaj o potencjalnym zastosowaniu w tle aktualizacji jednej lub większej ilości aplikacji",
|
"appsPossiblyUpdatedNotifDescription": "Powiadamia, gdy co najmniej jedna aktualizacja aplikacji została potencjalnie zastosowana w tle",
|
||||||
"xWasPossiblyUpdatedToY": "{} być może zaktualizowano do {}.",
|
"xWasPossiblyUpdatedToY": "{} być może zaktualizowano do {}.",
|
||||||
|
"enableBackgroundUpdates": "Włącz aktualizacje w tle",
|
||||||
"backgroundUpdateReqsExplanation": "Aktualizacje w tle mogą nie być możliwe dla wszystkich aplikacji.",
|
"backgroundUpdateReqsExplanation": "Aktualizacje w tle mogą nie być możliwe dla wszystkich aplikacji.",
|
||||||
"backgroundUpdateLimitsExplanation": "Powodzenie instalacji w tle można określić dopiero po otwarciu Obtainium.",
|
"backgroundUpdateLimitsExplanation": "Powodzenie instalacji w tle można określić dopiero po otwarciu Obtainium.",
|
||||||
"verifyLatestTag": "Zweryfikuj najnowszy tag",
|
"verifyLatestTag": "Zweryfikuj najnowszy tag",
|
||||||
|
"intermediateLinkRegex": "Filtruj link \"pośredni\" do odwiedzenia",
|
||||||
|
"filterByLinkText": "Filtruj linki według tekstu linku",
|
||||||
|
"intermediateLinkNotFound": "Nie znaleziono linku pośredniego",
|
||||||
|
"intermediateLink": "Łącze pośrednie",
|
||||||
"exemptFromBackgroundUpdates": "Wyklucz z uaktualnień w tle (jeśli są włączone)",
|
"exemptFromBackgroundUpdates": "Wyklucz z uaktualnień w tle (jeśli są włączone)",
|
||||||
"bgUpdatesOnWiFiOnly": "Wyłącz aktualizacje w tle, gdy nie ma połączenia z Wi-Fi",
|
"bgUpdatesOnWiFiOnly": "Wyłącz aktualizacje w tle, gdy nie ma połączenia z Wi-Fi",
|
||||||
"autoSelectHighestVersionCode": "Automatycznie wybierz najwyższy kod wersji APK",
|
"autoSelectHighestVersionCode": "Automatycznie wybierz najwyższy kod wersji APK",
|
||||||
"versionExtractionRegEx": "Wyrażenie regularne wyodrębniające wersję",
|
"versionExtractionRegEx": "Wyrażenie regularne wyodrębniające wersję",
|
||||||
"matchGroupToUse": "Dopasuj grupę do użycia",
|
"matchGroupToUse": "Dopasuj grupę do użycia dla wyrażenia regularnego wyodrębniania wersji",
|
||||||
"highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
|
"highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
|
||||||
"pickExportDir": "Wybierz katalog eksportu",
|
"pickExportDir": "Wybierz katalog eksportu",
|
||||||
"autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
|
"autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
|
||||||
|
"includeSettings": "Dołącz ustawienia",
|
||||||
"filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego",
|
"filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego",
|
||||||
"trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
|
"trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API",
|
||||||
"reverseSort": "Reverse sorting",
|
"reverseSort": "Odwrotne sortowanie",
|
||||||
"debugMenu": "Debug Menu",
|
"takeFirstLink": "Weź pierwszy link",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"skipSort": "Pomiń sortowanie",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"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",
|
||||||
|
"supportFixedAPKURL": "Obsługuj stałe adresy URL APK",
|
||||||
|
"selectX": "Wybierz {}",
|
||||||
|
"parallelDownloads": "Zezwól na pobieranie równoległe",
|
||||||
|
"installMethod": "Metoda instalacji",
|
||||||
|
"normal": "Normalna",
|
||||||
|
"root": "Źródło",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
|
"useSystemFont": "Użyj czcionki systemowej",
|
||||||
|
"systemFontError": "Błąd podczas ładowania czcionki systemowej: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Użyj kodu wersji aplikacji jako wersji wykrytej przez system operacyjny",
|
||||||
|
"requestHeader": "Nagłówek żądania",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Użyj najnowszego przesłanego zasobu jako daty wydania",
|
||||||
|
"defaultPseudoVersioningMethod": "Domyślna metoda pseudowersji",
|
||||||
|
"partialAPKHash": "Częściowy skrót APK",
|
||||||
|
"APKLinkHash": "Skrót łącza APK",
|
||||||
|
"directAPKLink": "Bezpośredni link APK",
|
||||||
|
"pseudoVersionInUse": "Pseudowersja jest w użyciu",
|
||||||
|
"installed": "Zainstalowano",
|
||||||
|
"latest": "Najnowszy",
|
||||||
|
"invertRegEx": "Odwróć wyrażenie regularne",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Usunąć aplikację?",
|
"one": "Usunąć aplikację?",
|
||||||
"few": "Usunąć aplikacje?",
|
"few": "Usunąć aplikacje?",
|
||||||
@@ -278,6 +312,12 @@
|
|||||||
"many": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} minut",
|
"many": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} minut",
|
||||||
"other": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} minuty"
|
"other": "Zbyt wiele żądań (ograniczona częstotliwość) - spróbuj ponownie za {} minuty"
|
||||||
},
|
},
|
||||||
|
"bgUpdateGotErrorRetryInMinutes": {
|
||||||
|
"one": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minutę",
|
||||||
|
"few": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minuty",
|
||||||
|
"many": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minut",
|
||||||
|
"other": "Sprawdzanie aktualizacji w tle napotkało {}, zaplanuje ponowne sprawdzenie za {} minuty"
|
||||||
|
},
|
||||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||||
"one": "W tle znaleziono {} aktualizację - w razie potrzeby użytkownik zostanie o tym powiadomiony",
|
"one": "W tle znaleziono {} aktualizację - w razie potrzeby użytkownik zostanie o tym powiadomiony",
|
||||||
"few": "W tle znaleziono {} aktualizacje - w razie potrzeby użytkownik zostanie o tym powiadomiony",
|
"few": "W tle znaleziono {} aktualizacje - w razie potrzeby użytkownik zostanie o tym powiadomiony",
|
||||||
@@ -337,8 +377,5 @@
|
|||||||
"few": "{} i {} inne apki mogły zostać zaktualizowane.",
|
"few": "{} i {} inne apki mogły zostać zaktualizowane.",
|
||||||
"many": "{} i {} innych apek mogło zostać zaktualizowanych.",
|
"many": "{} i {} innych apek mogło zostać zaktualizowanych.",
|
||||||
"other": "{} i {} inne apki mogły zostać zaktualizowane."
|
"other": "{} i {} inne apki mogły zostać zaktualizowane."
|
||||||
},
|
}
|
||||||
"enableBackgroundUpdates": "Włącz aktualizacje w tle",
|
}
|
||||||
"intermediateLinkRegex": "Filtr linków \"pośrednich\" do odwiedzenia w pierwszej kolejności",
|
|
||||||
"intermediateLinkNotFound": "Nie znaleziono linku pośredniego"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,287 +1,324 @@
|
|||||||
{
|
{
|
||||||
"invalidURLForSource": "URL {} inválida",
|
"invalidURLForSource": "URL {} inválida",
|
||||||
"noReleaseFound": "Não foi possivel encontrar uma versão adequada",
|
"noReleaseFound": "Não foi possível encontrar uma versão adequada",
|
||||||
"noVersionFound": "Não foi possivel encontrar uma versão lançada",
|
"noVersionFound": "Não foi possível encontrar uma versão",
|
||||||
"urlMatchesNoSource": "URL não corresponde a uma fonte conhecida",
|
"urlMatchesNoSource": "A URL não corresponde a uma fonte conhecida",
|
||||||
"cantInstallOlderVersion": "Não pode instalar uma versão anterior de um App",
|
"cantInstallOlderVersion": "Não é permitido instalar uma versão anterior de um aplicativo",
|
||||||
"appIdMismatch": "ID do pacote baixado não é igual ao ID do App instalado",
|
"appIdMismatch": "ID do pacote baixado não é igual ao ID do aplicativo instalado",
|
||||||
"functionNotImplemented": "Esta classe não implementou essa função",
|
"functionNotImplemented": "Esta classe não implementou essa função",
|
||||||
"placeholder": "Espaço Reservado",
|
"placeholder": "Espaço reservado",
|
||||||
"someErrors": "Alguns Erros Ocorreram",
|
"someErrors": "Alguns erros ocorreram",
|
||||||
"unexpectedError": "Erro Inesperado",
|
"unexpectedError": "Erro inesperado",
|
||||||
"ok": "Ok",
|
"ok": "OK",
|
||||||
"and": "e",
|
"and": "e",
|
||||||
"githubPATLabel": "Token de Acceso Pessoal do GitHub (Reduz tempos de espera)",
|
"githubPATLabel": "Token de acesso pessoal do GitHub (Reduz tempos de espera)",
|
||||||
"includePrereleases": "Incluir pré-lançamentos",
|
"includePrereleases": "Incluir pré-lançamentos",
|
||||||
"fallbackToOlderReleases": "Retornar para versões anteriores",
|
"fallbackToOlderReleases": "Retornar para versões anteriores",
|
||||||
"filterReleaseTitlesByRegEx": "Filtrar Titulos de Versões por Expressão Regular",
|
"filterReleaseTitlesByRegEx": "Filtrar títulos de versões usando expressão regular",
|
||||||
"invalidRegEx": "Expressão Regular Inválida",
|
"invalidRegEx": "Expressão regular inválida",
|
||||||
"noDescription": "Sem descrição",
|
"noDescription": "Sem descrição",
|
||||||
"cancel": "Cancelar",
|
"cancel": "Cancelar",
|
||||||
"continue": "Continuar",
|
"continue": "Continuar",
|
||||||
"requiredInBrackets": "(Necessário)",
|
"requiredInBrackets": "(Necessário)",
|
||||||
"dropdownNoOptsError": "ERRO: O DROPDOWN DEVE TER PELO MENOS UMA OPÇÃO",
|
"dropdownNoOptsError": "ERRO: O DROPDOWN DEVE TER PELO MENOS UMA OPÇÃO",
|
||||||
"colour": "Cor",
|
"colour": "Cor",
|
||||||
"githubStarredRepos": "Favoritados no GitHub",
|
"githubStarredRepos": "Com estrela no GitHub",
|
||||||
"uname": "Nome de usuário",
|
"uname": "Nome de usuário",
|
||||||
"wrongArgNum": "Número de argumentos errado",
|
"wrongArgNum": "Número de argumentos errado",
|
||||||
"xIsTrackOnly": "{} é 'Apenas Seguir'",
|
"xIsTrackOnly": "{} é 'Apenas para monitorar'",
|
||||||
"source": "Fonte",
|
"source": "Fonte",
|
||||||
"app": "App",
|
"app": "Aplicativo",
|
||||||
"appsFromSourceAreTrackOnly": "Os apps desta fonte são 'Apenas Seguir'.",
|
"appsFromSourceAreTrackOnly": "Os aplicativos desta fonte são 'Apenas para monitorar'.",
|
||||||
"youPickedTrackOnly": "Você selecionou a opção 'Apenas Seguir'.",
|
"youPickedTrackOnly": "Você selecionou a opção 'Apenas para monitorar'.",
|
||||||
"trackOnlyAppDescription": "Esse App vai ser seguido por atualizações, mais o Obtainium não poderá baixa-lo ou instala-lo.",
|
"trackOnlyAppDescription": "As atualizações desse aplicativo serão monitoradas, mas o Obtainium não poderá baixá-lo ou instalá-lo.",
|
||||||
"cancelled": "Cancelado",
|
"cancelled": "Cancelado",
|
||||||
"appAlreadyAdded": "App já adicionado",
|
"appAlreadyAdded": "Aplicativo já adicionado",
|
||||||
"alreadyUpToDateQuestion": "App já atualizado?",
|
"alreadyUpToDateQuestion": "Aplicativo já foi atualizado?",
|
||||||
"addApp": "Adicionar App",
|
"addApp": "Adicionar aplicativo",
|
||||||
"appSourceURL": "URL de origem do App",
|
"appSourceURL": "URL de origem do aplicativo",
|
||||||
"error": "Erro",
|
"error": "Erro",
|
||||||
"add": "Adicionar",
|
"add": "Adicionar",
|
||||||
"searchSomeSourcesLabel": "Procurar (Apenas Algumas Fontes)",
|
"searchSomeSourcesLabel": "Procurar (Apenas algumas fontes)",
|
||||||
"search": "Procurar",
|
"search": "Procurar",
|
||||||
"additionalOptsFor": "Opções Adicionais para {}",
|
"additionalOptsFor": "Opções adicionais para {}",
|
||||||
"supportedSources": "Fontes Compatíveis",
|
"supportedSources": "Fontes compatíveis",
|
||||||
"trackOnlyInBrackets": "(Apenas Seguir)",
|
"trackOnlyInBrackets": "(Apenas para monitorar)",
|
||||||
"searchableInBrackets": "(Pesquisável)",
|
"searchableInBrackets": "(Pesquisável)",
|
||||||
"appsString": "Apps",
|
"appsString": "Aplicativos",
|
||||||
"noApps": "Sem Apps",
|
"noApps": "Não há aplicativos",
|
||||||
"noAppsForFilter": "Sem Apps para Filtrar",
|
"noAppsForFilter": "Sem aplicativos para filtrar",
|
||||||
"byX": "Por {}",
|
"byX": "Por {}",
|
||||||
"percentProgress": "Progresso: {}%",
|
"percentProgress": "Progresso: {}%",
|
||||||
"pleaseWait": "Por Favor Espere",
|
"pleaseWait": "Por favor, espere",
|
||||||
"updateAvailable": "Atualização Disponível",
|
"updateAvailable": "Atualização disponível",
|
||||||
"estimateInBracketsShort": "(Aprox.)",
|
"notInstalled": "Não instalado",
|
||||||
"notInstalled": "Não Instalado",
|
"pseudoVersion": "pseudo-versão",
|
||||||
"estimateInBrackets": "(Aproximado)",
|
"selectAll": "Selecionar todos",
|
||||||
"selectAll": "Selecionar All",
|
"deselectX": "Deselecionar {}",
|
||||||
"deselectN": "Deselecionar {}",
|
"xWillBeRemovedButRemainInstalled": "{} será removido do Obtainium mais permanecerá instalado no dispositivo.",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} sera removido do Obtainium mais permanecerá instalado no dispositivo.",
|
"removeSelectedAppsQuestion": "Remover aplicativos selecionados?",
|
||||||
"removeSelectedAppsQuestion": "Remover Apps Selecionados?",
|
"removeSelectedApps": "Remover aplicativos selecionados",
|
||||||
"removeSelectedApps": "Remover Apps Selecionados",
|
|
||||||
"updateX": "Atualizar {}",
|
"updateX": "Atualizar {}",
|
||||||
"installX": "Instalar {}",
|
"installX": "Instalar {}",
|
||||||
"markXTrackOnlyAsUpdated": "Marcar {}\n(Apenas Seguir)\ncomo Atualizado",
|
"markXTrackOnlyAsUpdated": "Marcar {}\n(Apenas para monitorar)\ncomo Atualizado",
|
||||||
"changeX": "Mudar {}",
|
"changeX": "Mudar {}",
|
||||||
"installUpdateApps": "Instalar/Atualizar Apps",
|
"installUpdateApps": "Instalar/Atualizar aplicativos",
|
||||||
"installUpdateSelectedApps": "Instalar/Atualizar Apps Selecionados",
|
"installUpdateSelectedApps": "Instalar/Atualizar aplicativos selecionados",
|
||||||
"markXSelectedAppsAsUpdated": "Marcar {} Apps Delecionados como Atualizados?",
|
"markXSelectedAppsAsUpdated": "Marcar {} aplicativos selecionados como atualizados?",
|
||||||
"no": "Não",
|
"no": "Não",
|
||||||
"yes": "Sim",
|
"yes": "Sim",
|
||||||
"markSelectedAppsUpdated": "Marcar Apps Selecionados como Atualizados",
|
"markSelectedAppsUpdated": "Marcar aplicativos selecionados como Atualizados",
|
||||||
"pinToTop": "Fixar no topo",
|
"pinToTop": "Fixar no topo",
|
||||||
"unpinFromTop": "Desafixar do topo",
|
"unpinFromTop": "Desafixar do topo",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "Reiniciar Status de Instalação para Apps Seleciondos?",
|
"resetInstallStatusForSelectedAppsQuestion": "Reiniciar status de instalação nos aplicativos selecionados?",
|
||||||
"installStatusOfXWillBeResetExplanation": "O status de instalação de qualquer app selecionado sera reiniciado.\n\nIsso pode ajudar quando uma versão de um App mostrada no Obtainium é incorreta devido a falhas ao atualizar ou outros problemas.",
|
"installStatusOfXWillBeResetExplanation": "O status de instalação de todos os aplicativos selecionados será reiniciado.\n\nIsso pode ajudar quando uma versão de um aplicativo mostrada no Obtainium é incorreta devido a falhas ao atualizar ou outros problemas.",
|
||||||
"shareSelectedAppURLs": "Compartilhar URLs de Apps Selecionados",
|
"customLinkMessage": "Esses links funcionam em dispositivos com o Obtainium instalado",
|
||||||
"resetInstallStatus": "Reiniciar Status de Instalação",
|
"shareAppConfigLinks": "Compartilhar configuração do aplicativo como link HTML",
|
||||||
|
"shareSelectedAppURLs": "Compartilhar URLs de aplicativos selecionados",
|
||||||
|
"resetInstallStatus": "Reiniciar status de instalação",
|
||||||
"more": "Mais",
|
"more": "Mais",
|
||||||
"removeOutdatedFilter": "Remover Filtro de Apps Desatualizados",
|
"removeOutdatedFilter": "Remover filtro de aplicativos desatualizados",
|
||||||
"showOutdatedOnly": "Mostrar Apenas Apps Desatualizados",
|
"showOutdatedOnly": "Mostrar apenas aplicativos desatualizados",
|
||||||
"filter": "Filtro",
|
"filter": "Filtro",
|
||||||
"filterActive": "Filtro *",
|
"filterActive": "Filtro *",
|
||||||
"filterApps": "Filtrar Apps",
|
"filterApps": "Filtrar aplicativos",
|
||||||
"appName": "Nome do App",
|
"appName": "Nome do aplicativo",
|
||||||
"author": "Autor",
|
"author": "Autor",
|
||||||
"upToDateApps": "Apps Atualizados",
|
"upToDateApps": "Aplicativos atualizados",
|
||||||
"nonInstalledApps": "Apps Não Instalados",
|
"nonInstalledApps": "Aplicativos não instalados",
|
||||||
"importExport": "Importar/Exportar",
|
"importExport": "Importar/Exportar",
|
||||||
"settings": "Configurações",
|
"settings": "Configurações",
|
||||||
"exportedTo": "Exportado para {}",
|
"exportedTo": "Exportado para {}",
|
||||||
"obtainiumExport": "Exportar Obtainium",
|
"obtainiumExport": "Exportar Obtainium",
|
||||||
"invalidInput": "Input Inválido",
|
"invalidInput": "Entrada inválida",
|
||||||
"importedX": "Importado {}",
|
"importedX": "Importado {}",
|
||||||
"obtainiumImport": "Importar Obtainium",
|
"obtainiumImport": "Importar Obtainium",
|
||||||
"importFromURLList": "Importar de Lista de URLs",
|
"importFromURLList": "Importar de lista de URLs",
|
||||||
"searchQuery": "Pesquisa",
|
"searchQuery": "Pesquisa",
|
||||||
"appURLList": "Lista de URLs de Apps",
|
"appURLList": "Lista de URLs de aplicativos",
|
||||||
"line": "Linha",
|
"line": "Linha",
|
||||||
"searchX": "Pesquisa {}",
|
"searchX": "Pesquisa {}",
|
||||||
"noResults": "Nenhum resultado encontrado",
|
"noResults": "Nenhum resultado encontrado",
|
||||||
"importX": "Importar {}",
|
"importX": "Importar {}",
|
||||||
"importedAppsIdDisclaimer": "Apps Importados podem ser mostrados incorretamente como \"Não Instalado\".\nPara consertar, reinstale-os usando o Obtainium.\nIsso não deve afetar dados do App.\n\nAfeta apenas métodos de importação de URL e de terceiros.",
|
"importedAppsIdDisclaimer": "Aplicativos Importados podem ser mostrados incorretamente como \"Não Instalado\".\nPara consertar, reinstale-os usando o Obtainium.\nIsso não deve afetar dados do aplicativo.\n\nAfeta apenas métodos de importação de URL e de terceiros.",
|
||||||
"importErrors": "Erros de Importação",
|
"importErrors": "Erros de importação",
|
||||||
"importedXOfYApps": "{} de {} Apps importados.",
|
"importedXOfYApps": "{} de {} aplicativos importados.",
|
||||||
"followingURLsHadErrors": "As seguintes URLs apresentaram erros:",
|
"followingURLsHadErrors": "As seguintes URLs apresentaram erros:",
|
||||||
"okay": "Ok",
|
|
||||||
"selectURL": "Selecionar URL",
|
"selectURL": "Selecionar URL",
|
||||||
"selectURLs": "Selecionar URLs",
|
"selectURLs": "Selecionar URLs",
|
||||||
"pick": "Escolher",
|
"pick": "Escolher",
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"dark": "Escuro",
|
"dark": "Escuro",
|
||||||
"light": "Claro",
|
"light": "Claro",
|
||||||
"followSystem": "Seguir o Sistema",
|
"followSystem": "Seguir o sistema",
|
||||||
"obtainium": "Obtainium",
|
|
||||||
"materialYou": "Material You",
|
|
||||||
"useBlackTheme": "Usar tema preto completamente escuro",
|
"useBlackTheme": "Usar tema preto completamente escuro",
|
||||||
"appSortBy": "Classificar App por",
|
"appSortBy": "Classificar aplicativo por",
|
||||||
"authorName": "Autor/Nome",
|
"authorName": "Autor/Nome",
|
||||||
"nameAuthor": "Nome/Autor",
|
"nameAuthor": "Nome/Autor",
|
||||||
"asAdded": "Como Adicionado",
|
"asAdded": "Como adicionado",
|
||||||
"appSortOrder": "Ordem de classificação de Apps",
|
"appSortOrder": "Ordem de classificação de aplicativos",
|
||||||
"ascending": "Ascendente",
|
"ascending": "Ascendente",
|
||||||
"descending": "Descendente",
|
"descending": "Descendente",
|
||||||
"bgUpdateCheckInterval": "Intervalo de verificação de atualizações em segundo plano",
|
"bgUpdateCheckInterval": "Intervalo de verificação de atualizações em segundo-plano",
|
||||||
"neverManualOnly": "Nunca - Apenas Manual",
|
"neverManualOnly": "Nunca - apenas manual",
|
||||||
"appearance": "Aparência",
|
"appearance": "Aparência",
|
||||||
"showWebInAppView": "Mostrar páginas da internet em App view",
|
"showWebInAppView": "Mostrar página da web do aplicativo em informações do aplicativo",
|
||||||
"pinUpdates": "Fixar atualizações no topo da visão de Apps",
|
"pinUpdates": "Fixar atualizações no topo da janela de aplicativos",
|
||||||
"updates": "Atualizações",
|
"updates": "Atualizações",
|
||||||
"sourceSpecific": "Específico a fonte",
|
"sourceSpecific": "Específico a fonte",
|
||||||
"appSource": "Fonte de Apps",
|
"appSource": "Fonte do aplicativo",
|
||||||
"noLogs": "Sem Logs",
|
"noLogs": "Sem logs",
|
||||||
"appLogs": "Logs do App",
|
"appLogs": "Logs do aplicativo",
|
||||||
"close": "Fechar",
|
"close": "Fechar",
|
||||||
"share": "Compartilhar",
|
"share": "Compartilhar",
|
||||||
"appNotFound": "App não encontrado",
|
"appNotFound": "Aplicativo não encontrado",
|
||||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
"obtainiumExportHyphenatedLowercase": "exportação de obtainium",
|
||||||
"pickAnAPK": "Selecionar um APK",
|
"pickAnAPK": "Selecionar um APK",
|
||||||
"appHasMoreThanOnePackage": "{} tem mais de um pacote:",
|
"appHasMoreThanOnePackage": "{} tem mais de um pacote:",
|
||||||
"deviceSupportsXArch": "Seu dispositivo suporta a arquitetura de CPU {}.",
|
"deviceSupportsXArch": "Seu dispositivo suporta a arquitetura de CPU {}.",
|
||||||
"deviceSupportsFollowingArchs": "Seu dispositivo suporta as seguintes arquiteturas de CPU:",
|
"deviceSupportsFollowingArchs": "Seu dispositivo suporta as seguintes arquiteturas de CPU:",
|
||||||
"warning": "Aviso",
|
"warning": "Aviso",
|
||||||
"sourceIsXButPackageFromYPrompt": "A Fonte do App é '{}' mais o pacote lançado vem de '{}'. Continuar?",
|
"sourceIsXButPackageFromYPrompt": "A fonte do aplicativo é '{}' mas a origem do pacote é '{}'. Continuar?",
|
||||||
"updatesAvailable": "Atualizações Disponíveis",
|
"updatesAvailable": "Atualizações disponíveis",
|
||||||
"updatesAvailableNotifDescription": "Notifica o usuário quando atualizações estão disponíveis um ou mais Apps seguidos pelo Obtainium",
|
"updatesAvailableNotifDescription": "Notifica o usuário quando atualizações de um ou mais aplicativos monitorados pelo Obtainium estão disponíveis",
|
||||||
"noNewUpdates": "Sem novas atualizações.",
|
"noNewUpdates": "Sem novas atualizações.",
|
||||||
"xHasAnUpdate": "{} tem uma atualização.",
|
"xHasAnUpdate": "{} tem uma atualização.",
|
||||||
"appsUpdated": "Apps Atualizados",
|
"appsUpdated": "Aplicativos atualizados",
|
||||||
"appsUpdatedNotifDescription": "Notifica o usuário quando atualizações para um ou mais Apps foram aplicadas em segundo plano",
|
"appsUpdatedNotifDescription": "Notifica o usuário quando atualizações foram aplicadas em segundo-plano para um ou mais aplicativos ",
|
||||||
"xWasUpdatedToY": "{} foi atualizado para {}.",
|
"xWasUpdatedToY": "{} foi atualizado para {}.",
|
||||||
"errorCheckingUpdates": "Erro ao Procurar por Atualizações",
|
"errorCheckingUpdates": "Erro ao procurar por atualizações",
|
||||||
"errorCheckingUpdatesNotifDescription": "Uma notificação que mostra quando a checagem por atualizações em segundo plano falha",
|
"errorCheckingUpdatesNotifDescription": "Uma notificação que mostra quando a checagem por atualizações em segundo-plano falha",
|
||||||
"appsRemoved": "Apps Removidos",
|
"appsRemoved": "Aplicativos removidos",
|
||||||
"appsRemovedNotifDescription": "Notifica o usuário quando um ou mais Apps foram removidos devido a erros ao carregá-los",
|
"appsRemovedNotifDescription": "Notifica o usuário quando um ou mais aplicativos foram removidos devido a erros de carregamento",
|
||||||
"xWasRemovedDueToErrorY": "{} foi removido devido a este erro: {}",
|
"xWasRemovedDueToErrorY": "{} foi removido devido a este erro: {}",
|
||||||
"completeAppInstallation": "Instalação completa do App",
|
"completeAppInstallation": "Instalação do aplicativo concluída",
|
||||||
"obtainiumMustBeOpenToInstallApps": "Obtainium deve estar aberto para instalar Apps",
|
"obtainiumMustBeOpenToInstallApps": "Obtainium deve estar aberto para instalar os aplicativos",
|
||||||
"completeAppInstallationNotifDescription": "Pede ao usuário que retorne ao Obtainium para finalizar a instalação de um App",
|
"completeAppInstallationNotifDescription": "Pede ao usuário que retorne ao Obtainium para finalizar a instalação de um aplicativo",
|
||||||
"checkingForUpdates": "Checando por Atualizações",
|
"checkingForUpdates": "Verificando atualizações",
|
||||||
"checkingForUpdatesNotifDescription": "Notificação transiente que aparece quando checando por atualizações",
|
"checkingForUpdatesNotifDescription": "Notificação transiente que aparece quando o Obtainium está verificando se há atualizações",
|
||||||
"pleaseAllowInstallPerm": "Por favor, permita o Obtainium instalar Apps",
|
"pleaseAllowInstallPerm": "Por favor, permita que o Obtainium possa instalar aplicativos",
|
||||||
"trackOnly": "Apenas Seguir",
|
"trackOnly": "Apenas para monitorar",
|
||||||
"errorWithHttpStatusCode": "Erro {}",
|
"errorWithHttpStatusCode": "Erro {}",
|
||||||
"versionCorrectionDisabled": "Correção de versão desativada (plugin parece não funcionar)",
|
"versionCorrectionDisabled": "Correção de versão desativada (plugin parece não funcionar)",
|
||||||
"unknown": "Desconhecido",
|
"unknown": "Desconhecido",
|
||||||
"none": "Nenhum",
|
"none": "Nenhum",
|
||||||
"never": "Nunca",
|
"never": "Nunca",
|
||||||
"latestVersionX": "Última versão: {}",
|
"latestVersionX": "Última versão: {}",
|
||||||
"installedVersionX": "Versão Instalada: {}",
|
"installedVersionX": "Versão instalada: {}",
|
||||||
"lastUpdateCheckX": "Última Checagem por Atualização: {}",
|
"lastUpdateCheckX": "Última verificação de atualizações: {}",
|
||||||
"remove": "Remover",
|
"remove": "Remover",
|
||||||
"yesMarkUpdated": "Sim, Marcar como Atualizado",
|
"yesMarkUpdated": "Sim, marcar como atualizado",
|
||||||
"fdroid": "F-Droid Official",
|
"fdroid": "Oficial F-Droid",
|
||||||
"appIdOrName": "ID do App ou Nome",
|
"appIdOrName": "ID do aplicativo ou nome",
|
||||||
"appId": "ID do App",
|
"appId": "ID do aplicativo",
|
||||||
"appWithIdOrNameNotFound": "Nenhum App foi encontrado com esse ID ou nome",
|
"appWithIdOrNameNotFound": "Nenhum aplicativo foi encontrado com esse ID ou nome",
|
||||||
"reposHaveMultipleApps": "Repositórios podem conter multiplos Apps",
|
"reposHaveMultipleApps": "Repositórios podem conter múltiplos aplicativos",
|
||||||
"fdroidThirdPartyRepo": "Repositórios de terceiros F-Droid",
|
"fdroidThirdPartyRepo": "Repositórios de terceiros F-Droid",
|
||||||
"steam": "Steam",
|
"steamMobile": "Steam para celular",
|
||||||
"steamMobile": "Steam Mobile",
|
"steamChat": "Chat do Steam",
|
||||||
"steamChat": "Steam Chat",
|
|
||||||
"install": "Instalar",
|
"install": "Instalar",
|
||||||
"markInstalled": "Marcar Instalado",
|
"markInstalled": "Marcar instalado",
|
||||||
"update": "Atualizar",
|
"update": "Atualizar",
|
||||||
"markUpdated": "Marcar Atualizado",
|
"markUpdated": "Marcar como atualizado",
|
||||||
"additionalOptions": "Opções Adicionais",
|
"additionalOptions": "Opções adicionais",
|
||||||
"disableVersionDetection": "Desativar Detecção de Versão",
|
"disableVersionDetection": "Desativar detecção de versão",
|
||||||
"noVersionDetectionExplanation": "Essa opção deve apenas ser usada por Apps onde detecção de versão não funciona corretamente.",
|
"noVersionDetectionExplanation": "Essa opção deve apenas ser usada por aplicativos onde a detecção de versão não funciona corretamente.",
|
||||||
"downloadingX": "Baixando {}",
|
"downloadingX": "Baixando {}",
|
||||||
"downloadNotifDescription": "Notifica o usuário do progresso ao baixar um App",
|
"downloadNotifDescription": "Notifica o usuário o progresso do download de um aplicativo",
|
||||||
"noAPKFound": "APK não encontrado",
|
"noAPKFound": "APK não encontrado",
|
||||||
"noVersionDetection": "Sem Detecção de versão",
|
"noVersionDetection": "Sem detecção de versão",
|
||||||
"categorize": "Categorizar",
|
"categorize": "Categorizar",
|
||||||
"categories": "Categorias",
|
"categories": "Categorias",
|
||||||
"category": "Categoria",
|
"category": "Categoria",
|
||||||
"noCategory": "Sem Categoria",
|
"noCategory": "Sem categoria",
|
||||||
"noCategories": "Sem Categoria",
|
"noCategories": "Sem categorias",
|
||||||
"deleteCategoriesQuestion": "Deletar Categorias?",
|
"deleteCategoriesQuestion": "Deletar categorias?",
|
||||||
"categoryDeleteWarning": "Todos os Apps em categorias removidas serão descategorizados.",
|
"categoryDeleteWarning": "Todos os aplicativos em categorias removidas serão descategorizados.",
|
||||||
"addCategory": "Adicionar Categoria",
|
"addCategory": "Adicionar categoria",
|
||||||
"label": "Etiqueta",
|
"label": "Etiqueta",
|
||||||
"language": "Linguagem",
|
"language": "Linguagem",
|
||||||
"copiedToClipboard": "Copiado para a área de transferência",
|
"copiedToClipboard": "Copiado para a área de transferência",
|
||||||
"storagePermissionDenied": "Permição ao armazenamento negada",
|
"storagePermissionDenied": "Permissão de armazenamento negada",
|
||||||
"selectedCategorizeWarning": "Isso vai substituir qualquer confirução de categoria para os Apps selecionados.",
|
"selectedCategorizeWarning": "Isso vai substituir qualquer configuração de categoria para os aplicativos selecionados.",
|
||||||
"filterAPKsByRegEx": "Filtrar APKs por Expressão Regular",
|
"filterAPKsByRegEx": "Filtrar APKs usando expressão regular",
|
||||||
"removeFromObtainium": "Remover do Obtainium",
|
"removeFromObtainium": "Remover do Obtainium",
|
||||||
"uninstallFromDevice": "Desinstalar do dispositivo",
|
"uninstallFromDevice": "Desinstalar do dispositivo",
|
||||||
"onlyWorksWithNonVersionDetectApps": "Apenas funciona para Apps com detecção de versão desativada.",
|
"onlyWorksWithNonVersionDetectApps": "Apenas funciona para aplicativos com detecção de versão desativada.",
|
||||||
"releaseDateAsVersion": "Usar Data de Lançamento como Versão",
|
"releaseDateAsVersion": "Usar data de lançamento como versão",
|
||||||
"releaseDateAsVersionExplanation": "Esta opção só deve ser usada para aplicativos onde a detecção de versão não funciona corretamente, mas há uma data de lançamento disponível.",
|
"releaseDateAsVersionExplanation": "Esta opção só deve ser usada para aplicativos onde a detecção de versão não funciona corretamente, mas há uma data de lançamento disponível.",
|
||||||
"changes": "Mudanças",
|
"changes": "Mudanças",
|
||||||
"releaseDate": "Data de Lançamento",
|
"releaseDate": "Data de lançamento",
|
||||||
"importFromURLsInFile": "Importar de URLs em Arquivo (como OPML)",
|
"importFromURLsInFile": "Importar de URLs em arquivo (como OPML)",
|
||||||
"versionDetection": "Detecção de Versão",
|
"versionDetectionExplanation": "Reconciliar string de versão com versão detectada no sistema operacional",
|
||||||
|
"versionDetection": "Detecção de versão",
|
||||||
"standardVersionDetection": "Detecção de versão padrão",
|
"standardVersionDetection": "Detecção de versão padrão",
|
||||||
"groupByCategory": "Agroupar por Categoria",
|
"groupByCategory": "Agroupar por categoria",
|
||||||
"autoApkFilterByArch": "Tente filtrar APKs por arquitetura de CPU, se possível",
|
"autoApkFilterByArch": "Tente filtrar APKs por arquitetura de CPU, se possível",
|
||||||
"overrideSource": "Substituir Fonte",
|
"overrideSource": "Substituir fonte",
|
||||||
"dontShowAgain": "Não mostrar isso novamente",
|
"dontShowAgain": "Não mostrar isso novamente",
|
||||||
"dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas Seguir'",
|
"dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas para monitorar'",
|
||||||
"dontShowAPKOriginWarnings": "Não mostrar avisos de origem da APK",
|
"dontShowAPKOriginWarnings": "Não mostrar avisos de origem da APK",
|
||||||
"moveNonInstalledAppsToBottom": "Mover Apps não instalados para o fundo da visão de Apps",
|
"moveNonInstalledAppsToBottom": "Mover aplicativos não instalados para o fundo da lista de aplicativos",
|
||||||
"gitlabPATLabel": "Token de Acceso Pessoal do Gitlab\n(Ativa Pesquisa e Melhor Descoberta de APKs)",
|
"gitlabPATLabel": "Token de acesso pessoal do Gitlab\n(Ativa pesquisa e melhora a descoberta de APKs)",
|
||||||
"about": "Sobre",
|
"about": "Sobre",
|
||||||
"requiresCredentialsInSettings": "Isso requer credenciais adicionais (em Configurações)",
|
"requiresCredentialsInSettings": "{}: Isso requer credenciais adicionais (em Configurações)",
|
||||||
"checkOnStart": "Checar por atualizações ao iniciar ",
|
"checkOnStart": "Verificar se há atualizações ao iniciar",
|
||||||
"tryInferAppIdFromCode": "Tente inferir o ID do App pelo código fonte",
|
"tryInferAppIdFromCode": "Tente inferir o ID do aplicativo pelo código-fonte",
|
||||||
"removeOnExternalUninstall": "Remover automaticamente Apps desinstalados externamente",
|
"removeOnExternalUninstall": "Remover automaticamente aplicativos desinstalados externamente",
|
||||||
"pickHighestVersionCode": "Auto-selecionar o maior numero de versão do APK",
|
"pickHighestVersionCode": "Auto-selecionar o maior número de versão do APK",
|
||||||
"checkUpdateOnDetailPage": "Checar por atualizações ao abrir a pagina de detalhes de um App",
|
"checkUpdateOnDetailPage": "Checar por atualizações ao abrir a página de detalhes de um aplicativo",
|
||||||
"disablePageTransitions": "Desativar animações de transição de pagina",
|
"disablePageTransitions": "Desativar animações de transição de página",
|
||||||
"reversePageTransitions": "Reverter animações de transição de pagina",
|
"reversePageTransitions": "Reverter animações de transição de página",
|
||||||
"minStarCount": "Contagem Minima de Estrelas",
|
"minStarCount": "Contagem mínima de estrelas",
|
||||||
"addInfoBelow": "Adicionar essa informação abaixo.",
|
"addInfoBelow": "Adicionar essa informação abaixo.",
|
||||||
"addInfoInSettings": "Adicionar essa informação nas configurações.",
|
"addInfoInSettings": "Adicionar essa informação nas configurações.",
|
||||||
"githubSourceNote": "A limitação de taxa do GitHub pode ser evitada usando uma chave de API.",
|
"githubSourceNote": "A limitação de taxa do GitHub pode ser evitada usando uma chave de API.",
|
||||||
"gitlabSourceNote": "A extração de APK do GitLab pode não funcionar sem uma chave de API.",
|
"gitlabSourceNote": "A extração de endereço de download do APK no GitLab provavelmente não funcione sem que seja fornecido uma chave de API.",
|
||||||
"sortByFileNamesNotLinks": "Classifique por nomes de arquivos em vez de links completos",
|
"sortByLastLinkSegment": "Ordenar apenas usando o último segmento do link",
|
||||||
"filterReleaseNotesByRegEx": "Filtrar Notas de Lançamento por Expressão Regular",
|
"filterReleaseNotesByRegEx": "Filtrar notas de versão usando Regex",
|
||||||
"customLinkFilterRegex": "Filtro de Link Personalizado por Expressão Regular (Padrão '.apk$')",
|
"customLinkFilterRegex": "Filtro de link personalizado usando expressão regular (Padrão '.apk$')",
|
||||||
"appsPossiblyUpdated": "Tentativas de atualização de Apps",
|
"appsPossiblyUpdated": "Tentativas de atualização de aplicativos",
|
||||||
"appsPossiblyUpdatedNotifDescription": "Notifica o usuário de que atualizações de um ou mais Apps foram potencialmente aplicadas em segundo plano",
|
"appsPossiblyUpdatedNotifDescription": "Notifica o usuário de que atualizações de um ou mais aplicativos foram potencialmente aplicadas em segundo-plano",
|
||||||
"xWasPossiblyUpdatedToY": "{} pode ter sido atualizado para {}.",
|
"xWasPossiblyUpdatedToY": "{} pode ter sido atualizado para {}.",
|
||||||
"enableBackgroundUpdates": "Ativar atualizações em segundo plano",
|
"enableBackgroundUpdates": "Ativar atualizações em segundo-plano",
|
||||||
"backgroundUpdateReqsExplanation": "Atualizações em segundo plano podem não ser possíveis para todos os Apps.",
|
"backgroundUpdateReqsExplanation": "Atualizações em segundo-plano podem não ser possíveis para todos os aplicativos.",
|
||||||
"backgroundUpdateLimitsExplanation": "O sucesso de uma instalação em segundo plano só pode ser determinado quando o Obtainium é aberto.",
|
"backgroundUpdateLimitsExplanation": "O sucesso de uma instalação em segundo-plano só pode ser determinado quando o Obtainium é aberto.",
|
||||||
"verifyLatestTag": "Verifique a 'ultima' etiqueta",
|
"verifyLatestTag": "Verifique a 'última' etiqueta",
|
||||||
"intermediateLinkRegex": "Filtre por um Link 'Intermediário' para Visitar Primeiro",
|
"intermediateLinkRegex": "Filtrar um link 'intermediário' para visitar",
|
||||||
|
"filterByLinkText": "Filtrar links pelo texto do link",
|
||||||
"intermediateLinkNotFound": "Link intermediário não encontrado",
|
"intermediateLinkNotFound": "Link intermediário não encontrado",
|
||||||
"exemptFromBackgroundUpdates": "Isento de atualizações em segundo plano (se ativadas)",
|
"intermediateLink": "Link intermediário",
|
||||||
"bgUpdatesOnWiFiOnly": "Desative atualizações em segundo plano quando não estiver em WiFi",
|
"exemptFromBackgroundUpdates": "Isento de atualizações em segundo-plano (se ativadas)",
|
||||||
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
"bgUpdatesOnWiFiOnly": "Desative as atualizações em segundo-plano quando não estiver conectado no Wi-Fi",
|
||||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
"autoSelectHighestVersionCode": "Auto-selecionar a versão mais recente",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"versionExtractionRegEx": "Regex de extração de versão",
|
||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"matchGroupToUse": "Grupo correspondente a ser usado no Regex de extração de versão",
|
||||||
"pickExportDir": "Pick Export Directory",
|
"highlightTouchTargets": "Realçar áreas sensíveis ao toque que são menos óbvias",
|
||||||
"autoExportOnChanges": "Auto-export on changes",
|
"pickExportDir": "Escolher diretório para a exportação",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"autoExportOnChanges": "Auto-exportar em mudanças",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"includeSettings": "Incluir configurações",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"filterVersionsByRegEx": "Filtrar versões por expressão regular",
|
||||||
"reverseSort": "Reverse sorting",
|
"trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida",
|
||||||
"debugMenu": "Debug Menu",
|
"dontSortReleasesList": "Reter a ordem de lançamento da API",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"reverseSort": "Ordenação reversa",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"takeFirstLink": "Obter primeiro link",
|
||||||
|
"skipSort": "Ignorar ordenação",
|
||||||
|
"debugMenu": "Menu debug",
|
||||||
|
"bgTaskStarted": "Tarefa em segundo-plano iniciada - verifique os logs.",
|
||||||
|
"runBgCheckNow": "Execute agora em segundo-plano a verificação de atualizações",
|
||||||
|
"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": "Aplicativos atualizados",
|
||||||
|
"appsPossiblyUpdatedNotifChannel": "Tentativas de atualização de aplicativos",
|
||||||
|
"errorCheckingUpdatesNotifChannel": "Erro ao procurar por atualizações",
|
||||||
|
"appsRemovedNotifChannel": "Aplicativos removidos",
|
||||||
|
"downloadingXNotifChannel": "Baixando {}",
|
||||||
|
"completeAppInstallationNotifChannel": "Instalação completa do aplicativo",
|
||||||
|
"checkingForUpdatesNotifChannel": "Checando por atualizações",
|
||||||
|
"onlyCheckInstalledOrTrackOnlyApps": "Apenas verificar atualizações de aplicativos instalados e 'Apenas monitorar'",
|
||||||
|
"supportFixedAPKURL": "Suporte a APK com URLs fixas",
|
||||||
|
"selectX": "Selecionar {}",
|
||||||
|
"parallelDownloads": "Permitir downloads paralelos",
|
||||||
|
"installMethod": "Método de instalação",
|
||||||
|
"normal": "Normal",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "O Shizuku não está rodando",
|
||||||
|
"useSystemFont": "Usar fonte padrão do sistema",
|
||||||
|
"systemFontError": "Erro ao carregar a fonte do sistema: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Usar versionCode do aplicativo como versão detectada pelo sistema operacional",
|
||||||
|
"requestHeader": "Requisitar cabeçalho",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Use o último upload de recursos como data de lançamento",
|
||||||
|
"defaultPseudoVersioningMethod": "Método de pseudo-versionamento padrão",
|
||||||
|
"partialAPKHash": "Hash APK parcial",
|
||||||
|
"APKLinkHash": "Hash de links APK",
|
||||||
|
"directAPKLink": "Link direto do APK",
|
||||||
|
"pseudoVersionInUse": "Uma pseudo-versão está em uso",
|
||||||
|
"installed": "Instalado",
|
||||||
|
"latest": "Mais recente",
|
||||||
|
"invertRegEx": "Inverter expressão regular",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remover App?",
|
"one": "Remover aplicativo?",
|
||||||
"other": "Remover Apps?"
|
"other": "Remover aplicativos?"
|
||||||
},
|
},
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "Muitas solicitações (taxa limitada) - tente novamente em {} minuto",
|
"one": "Muitas solicitações (taxa de solicitações limitada) - tente novamente em {} minuto",
|
||||||
"other": "Muitas solicitações (taxa limitada) - tente novamente em {} minutos"
|
"other": "Muitas solicitações (taxa limitada) - tente novamente em {} minutos"
|
||||||
},
|
},
|
||||||
"bgUpdateGotErrorRetryInMinutes": {
|
"bgUpdateGotErrorRetryInMinutes": {
|
||||||
"one": "A verificação de atualizações em segundo plano encontrou um {}, agendada uma nova verificação em {} minuto",
|
"one": "A verificação de atualizações em segundo-plano encontrou um {}, agendada uma nova verificação em {} minuto",
|
||||||
"other": "A verificação de atualizações em segundo plano encontrou um {}, agendada uma nova verificação em {} minutos"
|
"other": "A verificação de atualizações em segundo-plano encontrou um {}, agendada uma nova verificação em {} minutos"
|
||||||
},
|
},
|
||||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||||
"one": "A verificação de atualizações em segundo plano encontrou {} atualização, o usuário sera notificado caso necessário",
|
"one": "A verificação de atualizações em segundo-plano encontrou {} atualização, o usuário sera notificado caso necessário",
|
||||||
"other": "A verificação de atualizações em segundo plano encontrou {} atualizações, o usuário sera notificado caso necessário"
|
"other": "A verificação de atualizações em segundo-plano encontrou {} atualizações, o usuário sera notificado caso necessário"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"one": "{} App",
|
"one": "{} Aplicativo",
|
||||||
"other": "{} Apps"
|
"other": "{} Aplicativos"
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"one": "{} URL",
|
"one": "{} URL",
|
||||||
@@ -300,19 +337,19 @@
|
|||||||
"other": "{} Dias"
|
"other": "{} Dias"
|
||||||
},
|
},
|
||||||
"clearedNLogsBeforeXAfterY": {
|
"clearedNLogsBeforeXAfterY": {
|
||||||
"one": "Limpo {n} log (antes = {antes}, depois = {depois})",
|
"one": "Foi limpo {n} log (antes = {antes}, depois = {depois})",
|
||||||
"other": "Limpados {n} logs (antes = {antes}, depois = {depois})"
|
"other": "Foram limpos {n} logs (antes = {antes}, depois = {depois})"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesAvailable": {
|
"xAndNMoreUpdatesAvailable": {
|
||||||
"one": "{} e 1 outro app tem atualizações.",
|
"one": "{} e um outro aplicativo possui atualizações.",
|
||||||
"other": "{} e {} outros apps tem atualizações."
|
"other": "{} e {} outros aplicativo possuem atualizações."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesInstalled": {
|
"xAndNMoreUpdatesInstalled": {
|
||||||
"one": "{} e 1 outro app foi atualizado.",
|
"one": "{} e um outro aplicativo foram atualizado.",
|
||||||
"other": "{} e {} outros apps foram atualizados."
|
"other": "{} e {} outros aplicativos foram atualizados."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
"one": "{} e 1 outro app pode ter sido atualizado.",
|
"one": "{} e um outro aplicativo podem ter sido atualizados.",
|
||||||
"other": "{} e {} outros apps podem ter sido atualizados."
|
"other": "{} e {} outros aplicativos podem ter sido atualizados."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"invalidURLForSource": "Неверный URL-адрес {} приложения",
|
"invalidURLForSource": "Неверный URL-адрес приложения: {}",
|
||||||
"noReleaseFound": "Не удалось найти подходящий релиз",
|
"noReleaseFound": "Не удалось найти подходящий релиз",
|
||||||
"noVersionFound": "Не удалось определить версию релиза",
|
"noVersionFound": "Не удалось определить версию релиза",
|
||||||
"urlMatchesNoSource": "URL-адрес не соответствует известному источнику",
|
"urlMatchesNoSource": "URL-адрес не соответствует известному источнику",
|
||||||
@@ -9,41 +9,41 @@
|
|||||||
"placeholder": "Заполнитель",
|
"placeholder": "Заполнитель",
|
||||||
"someErrors": "Возникли некоторые ошибки",
|
"someErrors": "Возникли некоторые ошибки",
|
||||||
"unexpectedError": "Неожиданная ошибка",
|
"unexpectedError": "Неожиданная ошибка",
|
||||||
"ok": "Окей",
|
"ok": "Ok",
|
||||||
"and": "и",
|
"and": "и",
|
||||||
"githubPATLabel": "Персональный токен доступа GitHub (увеличивает лимит запросов)",
|
"githubPATLabel": "Персональный токен доступа GitHub\n(увеличивает лимит запросов)",
|
||||||
"includePrereleases": "Включить предварительные релизы",
|
"includePrereleases": "Включить предварительные релизы",
|
||||||
"fallbackToOlderReleases": "Откатиться к более старым версиям",
|
"fallbackToOlderReleases": "Откатываться к предыдущей версии",
|
||||||
"filterReleaseTitlesByRegEx": "Фильтровать заголовки релизов\nс помощью регулярного выражения",
|
"filterReleaseTitlesByRegEx": "Фильтровать заголовки релизов\n(регулярное выражение)",
|
||||||
"invalidRegEx": "Неверное регулярное выражение",
|
"invalidRegEx": "Неверное регулярное выражение",
|
||||||
"noDescription": "Нет описания",
|
"noDescription": "Нет описания",
|
||||||
"cancel": "Отмена",
|
"cancel": "Отмена",
|
||||||
"continue": "Продолжить",
|
"continue": "Продолжить",
|
||||||
"requiredInBrackets": "(Обязательно)",
|
"requiredInBrackets": "(обязательно)",
|
||||||
"dropdownNoOptsError": "Ошибка: Выпадающий список должен содержать хотя бы одну опцию",
|
"dropdownNoOptsError": "Ошибка: в выпадающем списке должна быть выбрана хотя бы одна настройка",
|
||||||
"colour": "Цвет",
|
"colour": "Цвет",
|
||||||
"githubStarredRepos": "Помеченные звездочкой репозитории на GitHub",
|
"githubStarredRepos": "Избранные репозитории GitHub",
|
||||||
"uname": "Имя пользователя",
|
"uname": "Имя пользователя",
|
||||||
"wrongArgNum": "Неправильное количество предоставленных аргументов",
|
"wrongArgNum": "Неправильное количество предоставленных аргументов",
|
||||||
"xIsTrackOnly": "{} только для отслеживания",
|
"xIsTrackOnly": "{} только для отслеживания",
|
||||||
"source": "Источник",
|
"source": "Источник",
|
||||||
"app": "Приложение",
|
"app": "Приложение",
|
||||||
"appsFromSourceAreTrackOnly": "Приложения из этого источника являются 'только для отслеживания'.",
|
"appsFromSourceAreTrackOnly": "Приложения из этого источника настроены только для отслеживания",
|
||||||
"youPickedTrackOnly": "Вы выбрали опцию 'Только для отслеживания'.",
|
"youPickedTrackOnly": "Вы выбрали опцию 'Только для отслеживания'",
|
||||||
"trackOnlyAppDescription": "Приложение будет отслеживаться на предмет обновлений, но Obtainium не сможет загрузить или установить его.",
|
"trackOnlyAppDescription": "Приложение будет отслеживаться на предмет обновлений, но Obtainium не сможет загрузить или установить его",
|
||||||
"cancelled": "Отменено",
|
"cancelled": "Отменено",
|
||||||
"appAlreadyAdded": "Приложение уже добавлено",
|
"appAlreadyAdded": "Приложение уже добавлено",
|
||||||
"alreadyUpToDateQuestion": "Приложение уже обновлено?",
|
"alreadyUpToDateQuestion": "Приложение уже обновлено?",
|
||||||
"addApp": "Добавить приложение",
|
"addApp": "Добавить",
|
||||||
"appSourceURL": "URL-источник приложения",
|
"appSourceURL": "URL-источник приложения",
|
||||||
"error": "Ошибка",
|
"error": "Ошибка",
|
||||||
"add": "Добавить",
|
"add": "Добавить",
|
||||||
"searchSomeSourcesLabel": "Поиск (только в некоторых источниках)",
|
"searchSomeSourcesLabel": "Поиск (в некоторых источниках)",
|
||||||
"search": "Поиск",
|
"search": "Поиск",
|
||||||
"additionalOptsFor": "Дополнительные опции для {}",
|
"additionalOptsFor": "Дополнительные настройки для {}",
|
||||||
"supportedSources": "Поддерживаемые источники",
|
"supportedSources": "Поддерживаемые источники",
|
||||||
"trackOnlyInBrackets": "(Только для отслеживания)",
|
"trackOnlyInBrackets": "(только отслеживание)",
|
||||||
"searchableInBrackets": "(Поиск)",
|
"searchableInBrackets": "(поиск)",
|
||||||
"appsString": "Приложения",
|
"appsString": "Приложения",
|
||||||
"noApps": "Нет приложений",
|
"noApps": "Нет приложений",
|
||||||
"noAppsForFilter": "Нет приложений для фильтра",
|
"noAppsForFilter": "Нет приложений для фильтра",
|
||||||
@@ -51,12 +51,11 @@
|
|||||||
"percentProgress": "Прогресс: {}%",
|
"percentProgress": "Прогресс: {}%",
|
||||||
"pleaseWait": "Пожалуйста, подождите",
|
"pleaseWait": "Пожалуйста, подождите",
|
||||||
"updateAvailable": "Доступно обновление",
|
"updateAvailable": "Доступно обновление",
|
||||||
"estimateInBracketsShort": "(Оценка)",
|
|
||||||
"notInstalled": "Не установлено",
|
"notInstalled": "Не установлено",
|
||||||
"estimateInBrackets": "(Оценка)",
|
"pseudoVersion": "псевдоверсия",
|
||||||
"selectAll": "Выбрать все",
|
"selectAll": "Выбрать всё",
|
||||||
"deselectN": "Отменить выбор {}",
|
"deselectX": "Отменить выбор {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} будет удалено из Obtainium, но останется установленным на устройстве.",
|
"xWillBeRemovedButRemainInstalled": "{} будет удалено из Obtainium, но останется на устройстве",
|
||||||
"removeSelectedAppsQuestion": "Удалить выбранные приложения?",
|
"removeSelectedAppsQuestion": "Удалить выбранные приложения?",
|
||||||
"removeSelectedApps": "Удалить выбранные приложения",
|
"removeSelectedApps": "Удалить выбранные приложения",
|
||||||
"updateX": "Обновить {}",
|
"updateX": "Обновить {}",
|
||||||
@@ -65,17 +64,19 @@
|
|||||||
"changeX": "Изменить {}",
|
"changeX": "Изменить {}",
|
||||||
"installUpdateApps": "Установить/Обновить приложения",
|
"installUpdateApps": "Установить/Обновить приложения",
|
||||||
"installUpdateSelectedApps": "Установить/Обновить выбранные приложения",
|
"installUpdateSelectedApps": "Установить/Обновить выбранные приложения",
|
||||||
"markXSelectedAppsAsUpdated": "Отметить {} выбранные приложения как обновленные?",
|
"markXSelectedAppsAsUpdated": "Выбрано приложений: {}. Отметить как обновлённые?",
|
||||||
"no": "Нет",
|
"no": "Нет",
|
||||||
"yes": "Да",
|
"yes": "Да",
|
||||||
"markSelectedAppsUpdated": "Отметить выбранные приложения как обновленные",
|
"markSelectedAppsUpdated": "Отметить выбранные приложения как обновлённые",
|
||||||
"pinToTop": "Закрепить сверху",
|
"pinToTop": "Закрепить сверху",
|
||||||
"unpinFromTop": "Открепить",
|
"unpinFromTop": "Открепить",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "Сбросить статус установки для выбранных приложений?",
|
"resetInstallStatusForSelectedAppsQuestion": "Сбросить статус установки для выбранных приложений?",
|
||||||
"installStatusOfXWillBeResetExplanation": "Статус установки для выбранных приложений будет сброшен.\n\nЭто может помочь, если версия приложения, отображаемая в Obtainium, неправильная из-за неудачных обновлений или других проблем.",
|
"installStatusOfXWillBeResetExplanation": "Статус установки для выбранных приложений будет сброшен.\n\nЭто может помочь, если версия приложения, отображаемая в Obtainium, некорректная — из-за неудачных обновлений или других проблем",
|
||||||
|
"customLinkMessage": "Эти ссылки работают на устройствах с установленным Obtainium",
|
||||||
|
"shareAppConfigLinks": "Поделиться конфигурацией приложения в виде HTML-ссылки",
|
||||||
"shareSelectedAppURLs": "Поделиться выбранными URL-адресами приложений",
|
"shareSelectedAppURLs": "Поделиться выбранными URL-адресами приложений",
|
||||||
"resetInstallStatus": "Сбросить статус установки",
|
"resetInstallStatus": "Сбросить статус установки",
|
||||||
"more": "Еще",
|
"more": "Ещё",
|
||||||
"removeOutdatedFilter": "Удалить фильтр для устаревших приложений",
|
"removeOutdatedFilter": "Удалить фильтр для устаревших приложений",
|
||||||
"showOutdatedOnly": "Показывать только устаревшие приложения",
|
"showOutdatedOnly": "Показывать только устаревшие приложения",
|
||||||
"filter": "Фильтр",
|
"filter": "Фильтр",
|
||||||
@@ -85,7 +86,7 @@
|
|||||||
"author": "Автор",
|
"author": "Автор",
|
||||||
"upToDateApps": "Приложения со свежими обновлениями",
|
"upToDateApps": "Приложения со свежими обновлениями",
|
||||||
"nonInstalledApps": "Неустановленные приложения",
|
"nonInstalledApps": "Неустановленные приложения",
|
||||||
"importExport": "Импорт/экспорт",
|
"importExport": "Данные",
|
||||||
"settings": "Настройки",
|
"settings": "Настройки",
|
||||||
"exportedTo": "Экспортировано в {}",
|
"exportedTo": "Экспортировано в {}",
|
||||||
"obtainiumExport": "Экспорт из Obtainium",
|
"obtainiumExport": "Экспорт из Obtainium",
|
||||||
@@ -99,63 +100,60 @@
|
|||||||
"searchX": "Поиск {}",
|
"searchX": "Поиск {}",
|
||||||
"noResults": "Результатов не найдено",
|
"noResults": "Результатов не найдено",
|
||||||
"importX": "Импорт {}",
|
"importX": "Импорт {}",
|
||||||
"importedAppsIdDisclaimer": "Импортированные приложения могут неверно отображаться как 'Не установлены'.\nДля исправления этой проблемы повторно установите их через Obtainium.\nЭто не должно повлиять на данные приложения.\n\nПроблемы возникают только при импорте из URL-адреса и сторонних источников.",
|
"importedAppsIdDisclaimer": "Импортированные приложения могут неверно отображаться как неустановленные.\nДля исправления этой проблемы повторно установите их через Obtainium.\nЭто не должно повлиять на данные приложения.\n\nПроблемы возникают только при импорте из URL-адреса и сторонних источников",
|
||||||
"importErrors": "Ошибка импорта",
|
"importErrors": "Ошибка импорта",
|
||||||
"importedXOfYApps": "Импортировано {} из {} приложений.",
|
"importedXOfYApps": "Импортировано приложений: {} из {}",
|
||||||
"followingURLsHadErrors": "При импорте следующие URL-адреса содержали ошибки:",
|
"followingURLsHadErrors": "При импорте следующие URL-адреса содержали ошибки:",
|
||||||
"okay": "Окей",
|
|
||||||
"selectURL": "Выбрать URL-адрес",
|
"selectURL": "Выбрать URL-адрес",
|
||||||
"selectURLs": "Выбрать URL-адреса",
|
"selectURLs": "Выбрать URL-адреса",
|
||||||
"pick": "Выбрать",
|
"pick": "Выбрать",
|
||||||
"theme": "Тема",
|
"theme": "Тема",
|
||||||
"dark": "Темная",
|
"dark": "Тёмная",
|
||||||
"light": "Светлая",
|
"light": "Светлая",
|
||||||
"followSystem": "Как в системе",
|
"followSystem": "Системная",
|
||||||
"obtainium": "Obtainium",
|
|
||||||
"materialYou": "Material You",
|
|
||||||
"useBlackTheme": "Использовать чёрную тему",
|
"useBlackTheme": "Использовать чёрную тему",
|
||||||
"appSortBy": "Сортировка приложений по",
|
"appSortBy": "Сортировка приложений",
|
||||||
"authorName": "Автор/Название",
|
"authorName": "Автор/Название",
|
||||||
"nameAuthor": "Название/Автор",
|
"nameAuthor": "Название/Автор",
|
||||||
"asAdded": "В порядке добавления",
|
"asAdded": "В порядке добавления",
|
||||||
"appSortOrder": "Порядок сортировки приложений",
|
"appSortOrder": "Порядок",
|
||||||
"ascending": "По возрастанию",
|
"ascending": "По возрастанию",
|
||||||
"descending": "По убыванию",
|
"descending": "По убыванию",
|
||||||
"bgUpdateCheckInterval": "Интервал проверки обновлений в фоновом режиме",
|
"bgUpdateCheckInterval": "Интервал проверки обновлений в фоновом режиме",
|
||||||
"neverManualOnly": "Никогда - Только вручную",
|
"neverManualOnly": "Никогда — только вручную",
|
||||||
"appearance": "Внешний вид",
|
"appearance": "Внешний вид",
|
||||||
"showWebInAppView": "Показывать исходную веб-страницу в представлении приложения",
|
"showWebInAppView": "Показывать исходную веб-страницу на странице приложения",
|
||||||
"pinUpdates": "Закрепить обновления сверху списка приложений",
|
"pinUpdates": "Отображать обновления приложений сверху списка",
|
||||||
"updates": "Обновления",
|
"updates": "Обновления",
|
||||||
"sourceSpecific": "Специфика источника",
|
"sourceSpecific": "Настройки источников",
|
||||||
"appSource": "Источник приложения",
|
"appSource": "Исходный код",
|
||||||
"noLogs": "Нет журналов",
|
"noLogs": "Нет журналов",
|
||||||
"appLogs": "Журналы приложений",
|
"appLogs": "Логи",
|
||||||
"close": "Закрыть",
|
"close": "Закрыть",
|
||||||
"share": "Поделиться",
|
"share": "Поделиться",
|
||||||
"appNotFound": "Приложение не найдено",
|
"appNotFound": "Приложение не найдено",
|
||||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
"obtainiumExportHyphenatedLowercase": "получение-экспорт",
|
||||||
"pickAnAPK": "Выберите APK-файл",
|
"pickAnAPK": "Выберите APK-файл",
|
||||||
"appHasMoreThanOnePackage": "{} имеет более одного пакета:",
|
"appHasMoreThanOnePackage": "{} имеет более одного пакета:",
|
||||||
"deviceSupportsXArch": "Ваше устройство поддерживает архитектуру процессора {}.",
|
"deviceSupportsXArch": "Ваше устройство поддерживает архитектуру процессора {}",
|
||||||
"deviceSupportsFollowingArchs": "Ваше устройство поддерживает следующие архитектуры процессора:",
|
"deviceSupportsFollowingArchs": "Ваше устройство поддерживает следующие архитектуры процессора:",
|
||||||
"warning": "Предупреждение",
|
"warning": "Предупреждение",
|
||||||
"sourceIsXButPackageFromYPrompt": "Источник приложения - '{}', но пакет для установки получен из '{}'. Продолжить?",
|
"sourceIsXButPackageFromYPrompt": "Источник приложения — '{}', но пакет для установки получен из '{}'. Продолжить?",
|
||||||
"updatesAvailable": "Доступны обновления",
|
"updatesAvailable": "Доступны обновления",
|
||||||
"updatesAvailableNotifDescription": "Уведомляет пользователя о наличии обновлений для одного или нескольких приложений, отслеживаемых Obtainium",
|
"updatesAvailableNotifDescription": "Уведомляет о наличии обновлений для одного или нескольких приложений в Obtainium",
|
||||||
"noNewUpdates": "Нет новых обновлений.",
|
"noNewUpdates": "Нет новых обновлений",
|
||||||
"xHasAnUpdate": "{} есть обновление.",
|
"xHasAnUpdate": "{} есть обновление",
|
||||||
"appsUpdated": "Приложения обновлены",
|
"appsUpdated": "Приложения обновлены",
|
||||||
"appsUpdatedNotifDescription": "Уведомляет пользователя о том, что обновления для одного или нескольких приложений были применены в фоновом режиме",
|
"appsUpdatedNotifDescription": "Уведомляет об обновлении одного или нескольких приложений в фоновом режиме",
|
||||||
"xWasUpdatedToY": "{} была обновлена до версии {}.",
|
"xWasUpdatedToY": "{} была обновлена до версии {}",
|
||||||
"errorCheckingUpdates": "Ошибка при проверке обновлений",
|
"errorCheckingUpdates": "Ошибка при проверке обновлений",
|
||||||
"errorCheckingUpdatesNotifDescription": "Уведомление, которое появляется, когда проверка обновлений в фоновом режиме завершилась с ошибкой",
|
"errorCheckingUpdatesNotifDescription": "Уведомление о завершении проверки обновлений в фоновом режиме с ошибкой",
|
||||||
"appsRemoved": "Приложение удалено",
|
"appsRemoved": "Приложение удалено",
|
||||||
"appsRemovedNotifDescription": "Уведомляет пользователя о том, что одно или несколько приложений было удалено из-за ошибок при их загрузке",
|
"appsRemovedNotifDescription": "Уведомление об удалении одного или несколько приложений из-за ошибок при их загрузке",
|
||||||
"xWasRemovedDueToErrorY": "{} был удален из-за ошибки: {}",
|
"xWasRemovedDueToErrorY": "{} был удален из-за ошибки: {}",
|
||||||
"completeAppInstallation": "Завершение установки приложения",
|
"completeAppInstallation": "Завершение установки приложения",
|
||||||
"obtainiumMustBeOpenToInstallApps": "Для установки приложений Obtainium должен быть открыт",
|
"obtainiumMustBeOpenToInstallApps": "Obtainium должен быть открыт для установки приложений",
|
||||||
"completeAppInstallationNotifDescription": "Просит пользователя вернуться в Obtainium, чтобы завершить установку приложения",
|
"completeAppInstallationNotifDescription": "Уведомление о необходимости открыть Obtainium для завершения установки приложения",
|
||||||
"checkingForUpdates": "Проверка обновлений",
|
"checkingForUpdates": "Проверка обновлений",
|
||||||
"checkingForUpdatesNotifDescription": "Временное уведомление, которое появляется при проверке обновлений",
|
"checkingForUpdatesNotifDescription": "Временное уведомление, которое появляется при проверке обновлений",
|
||||||
"pleaseAllowInstallPerm": "Пожалуйста, разрешите Obtainium устанавливать приложения",
|
"pleaseAllowInstallPerm": "Пожалуйста, разрешите Obtainium устанавливать приложения",
|
||||||
@@ -167,149 +165,191 @@
|
|||||||
"never": "Никогда",
|
"never": "Никогда",
|
||||||
"latestVersionX": "Последняя версия: {}",
|
"latestVersionX": "Последняя версия: {}",
|
||||||
"installedVersionX": "Установленная версия: {}",
|
"installedVersionX": "Установленная версия: {}",
|
||||||
"lastUpdateCheckX": "Последняя проверка обновлений: {}",
|
"lastUpdateCheckX": "Последняя проверка: {}",
|
||||||
"remove": "Удалить",
|
"remove": "Удалить",
|
||||||
"yesMarkUpdated": "Да, отметить как обновленное",
|
"yesMarkUpdated": "Да, отметить как обновленное",
|
||||||
"fdroid": "Официальный F-Droid",
|
"fdroid": "Официальные репозитории F-Droid",
|
||||||
"appIdOrName": "ID или название приложения",
|
"appIdOrName": "ID или название приложения",
|
||||||
"appId": "ID приложения",
|
"appId": "ID приложения",
|
||||||
"appWithIdOrNameNotFound": "Приложение с таким ID или названием не было найдено",
|
"appWithIdOrNameNotFound": "Приложение с таким ID или названием не было найдено",
|
||||||
"reposHaveMultipleApps": "В хранилище может быть несколько приложений",
|
"reposHaveMultipleApps": "В хранилище несколько приложений",
|
||||||
"fdroidThirdPartyRepo": "Сторонние репозитории F-Droid",
|
"fdroidThirdPartyRepo": "Сторонние репозитории F-Droid",
|
||||||
"steam": "Steam",
|
"steamMobile": "Стим Мобайл",
|
||||||
"steamMobile": "Steam Mobile",
|
"steamChat": "Стим-чат",
|
||||||
"steamChat": "Steam Chat",
|
|
||||||
"install": "Установить",
|
"install": "Установить",
|
||||||
"markInstalled": "Пометить как установленное",
|
"markInstalled": "Пометить как установленное",
|
||||||
"update": "Обновить",
|
"update": "Обновить",
|
||||||
"markUpdated": "Отметить обновленным",
|
"markUpdated": "Отметить обновлённым",
|
||||||
"additionalOptions": "Дополнительные опции",
|
"additionalOptions": "Дополнительные настройки",
|
||||||
"disableVersionDetection": "Отключить обнаружение версии",
|
"disableVersionDetection": "Отключить обнаружение версии",
|
||||||
"noVersionDetectionExplanation": "Эта опция должна использоваться только для приложений, где обнаружение версии не работает корректно.",
|
"noVersionDetectionExplanation": "Эта настройка должна использоваться только для приложений, где обнаружение версии не работает корректно",
|
||||||
"downloadingX": "Загрузка {}",
|
"downloadingX": "Загрузка {}",
|
||||||
"downloadNotifDescription": "Уведомляет пользователя о прогрессе загрузки приложения",
|
"downloadNotifDescription": "Уведомляет пользователя о прогрессе загрузки приложения",
|
||||||
"noAPKFound": "APK не найден",
|
"noAPKFound": "APK не найден",
|
||||||
"noVersionDetection": "Версий не обнаружено",
|
"noVersionDetection": "Обнаружение версий отключено",
|
||||||
"categorize": "Категоризировать",
|
"categorize": "Категоризировать",
|
||||||
"categories": "Категории",
|
"categories": "Категории",
|
||||||
"category": "Категория",
|
"category": "Категория",
|
||||||
"noCategory": "Без категории",
|
"noCategory": "Без категории",
|
||||||
"noCategories": "Без категорий",
|
"noCategories": "Без категорий",
|
||||||
"deleteCategoriesQuestion": "Удалить категории?",
|
"deleteCategoriesQuestion": "Удалить категории?",
|
||||||
"categoryDeleteWarning": "Все приложения в удаленных категориях будут помечены как без категории.",
|
"categoryDeleteWarning": "Все приложения в удаленных категориях будут помечены как без категории",
|
||||||
"addCategory": "Добавить категорию",
|
"addCategory": "Добавить категорию",
|
||||||
"label": "Метка",
|
"label": "Метка",
|
||||||
"language": "Язык",
|
"language": "Язык",
|
||||||
"copiedToClipboard": "Скопировано в буфер обмена",
|
"copiedToClipboard": "Скопировано в буфер обмена",
|
||||||
"storagePermissionDenied": "Отказано в доступе к хранилищу",
|
"storagePermissionDenied": "Отказано в доступе к хранилищу",
|
||||||
"selectedCategorizeWarning": "Это заменит все текущие настройки категорий для выбранных приложений.",
|
"selectedCategorizeWarning": "Это заменит все текущие настройки категорий для выбранных приложений",
|
||||||
"filterAPKsByRegEx": "Фильтровать APK-файлы с помощью\nрегулярного выражения",
|
"filterAPKsByRegEx": "Отфильтровать APK-файлы\n(регулярное выражение)",
|
||||||
"removeFromObtainium": "Удалить из Obtainium",
|
"removeFromObtainium": "Удалить из Obtainium",
|
||||||
"uninstallFromDevice": "Удалить с устройства",
|
"uninstallFromDevice": "Удалить с устройства",
|
||||||
"onlyWorksWithNonVersionDetectApps": "Работает только для приложений с отключенным определением версии.",
|
"onlyWorksWithNonVersionDetectApps": "Работает только для приложений с отключенным определением версии",
|
||||||
"releaseDateAsVersion": "Использовать дату выпуска в качестве версии",
|
"releaseDateAsVersion": "Дата выпуска вместо версии",
|
||||||
"releaseDateAsVersionExplanation": "Этот параметр следует использовать только для приложений, в которых определение версии не работает правильно, но имеется дата выпуска.",
|
"releaseDateAsVersionExplanation": "Этот параметр следует использовать только для приложений, в которых определение версии не работает правильно, но имеется дата выпуска",
|
||||||
"changes": "Изменения",
|
"changes": "Изменения",
|
||||||
"releaseDate": "Дата выпуска",
|
"releaseDate": "Дата выпуска",
|
||||||
"importFromURLsInFile": "Импорт URL-адресов из файла (например, OPML)",
|
"importFromURLsInFile": "Импорт из файла URL-адресов (например: OPML)",
|
||||||
|
"versionDetectionExplanation": "Согласовать строку версии с версией, обнаруженной в ОС",
|
||||||
"versionDetection": "Определение версии",
|
"versionDetection": "Определение версии",
|
||||||
"standardVersionDetection": "Стандартное определение версии",
|
"standardVersionDetection": "Стандартное",
|
||||||
"groupByCategory": "Группировать по категориям",
|
"groupByCategory": "Группировать по категориям",
|
||||||
"autoApkFilterByArch": "Попытка фильтрации APK-файлов по архитектуре процессора, если это возможно",
|
"autoApkFilterByArch": "Попытаться отфильтровать APK-файлы по архитектуре процессора",
|
||||||
"overrideSource": "Переопределить источник",
|
"overrideSource": "Переопределить источник",
|
||||||
"dontShowAgain": "Не показывать снова",
|
"dontShowAgain": "Не показывать снова",
|
||||||
"dontShowTrackOnlyWarnings": "Не показывать предупреждения о только отслеживаемых приложениях",
|
"dontShowTrackOnlyWarnings": "Не показывать предупреждения о только отслеживаемых приложениях",
|
||||||
"dontShowAPKOriginWarnings": "Не показывать предупреждения об источнике APK-файлов",
|
"dontShowAPKOriginWarnings": "Не показывать предупреждения об отличающемся источнике APK-файлов",
|
||||||
"moveNonInstalledAppsToBottom": "Переместить неустановленные приложения вниз списка",
|
"moveNonInstalledAppsToBottom": "Отображать неустановленные приложения внизу списка",
|
||||||
"gitlabPATLabel": "Персональный токен доступа GitLab\n(Включает поиск и улучшает обнаружение APK)",
|
"gitlabPATLabel": "Персональный токен доступа GitLab\n(включает поиск и улучшает обнаружение APK)",
|
||||||
"about": "О приложении",
|
"about": "Описание",
|
||||||
"requiresCredentialsInSettings": "Для этого требуются дополнительные учетные данные (в настройках)",
|
"requiresCredentialsInSettings": "{}: Для этого требуются дополнительные учетные данные (в настройках)",
|
||||||
"checkOnStart": "Проверять наличие обновлений при запуске",
|
"checkOnStart": "Проверять наличие обновлений при запуске",
|
||||||
"tryInferAppIdFromCode": "Попытаться определить ID приложения из исходного кода",
|
"tryInferAppIdFromCode": "Попытаться определить ID приложения из исходного кода",
|
||||||
"removeOnExternalUninstall": "Автоматически убирать из списка удаленные извне приложения",
|
"removeOnExternalUninstall": "Автоматически убирать из списка удаленные извне приложения",
|
||||||
"pickHighestVersionCode": "Автовыбор кода наивысшей версии APK",
|
"pickHighestVersionCode": "Автовыбор актуальной версии кода APK",
|
||||||
"checkUpdateOnDetailPage": "Проверять наличие обновлений при открытии страницы представления приложения",
|
"checkUpdateOnDetailPage": "Проверять наличие обновлений при открытии страницы приложения",
|
||||||
"disablePageTransitions": "Отключить анимацию перехода между страницами",
|
"disablePageTransitions": "Отключить анимацию перехода между страницами",
|
||||||
"reversePageTransitions": "Реверс анимации перехода между страницами",
|
"reversePageTransitions": "Реверс анимации перехода между страницами",
|
||||||
"minStarCount": "Минимальное количество звёзд",
|
"minStarCount": "Минимальное количество звёзд",
|
||||||
"addInfoBelow": "Добавьте эту информацию ниже.",
|
"addInfoBelow": "Добавьте эту информацию ниже",
|
||||||
"addInfoInSettings": "Добавьте эту информацию в Настройки.",
|
"addInfoInSettings": "Добавьте эту информацию в Настройки",
|
||||||
"githubSourceNote": "Лимит запросов GitHub можно обойти, используя ключ API.",
|
"githubSourceNote": "Используя ключ API можно обойти лимит запросов GitHub",
|
||||||
"gitlabSourceNote": "Извлечение APK из GitLab может не работать без ключа API.",
|
"gitlabSourceNote": "Без ключа API может не работать извлечение APK с GitLab",
|
||||||
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
|
"sortByLastLinkSegment": "Сортировать только по последнему сегменту ссылки",
|
||||||
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
|
"filterReleaseNotesByRegEx": "Фильтровать примечания к выпуску\n(регулярное выражение)",
|
||||||
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
|
"customLinkFilterRegex": "Пользовательский фильтр ссылок APK\n(регулярное выражение, по умолчанию: '.apk$')",
|
||||||
"appsPossiblyUpdated": "App Updates Attempted",
|
"appsPossiblyUpdated": "Попытки обновления приложений",
|
||||||
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
|
"appsPossiblyUpdatedNotifDescription": "Уведомление о возможных обновлениях одного или нескольких приложений в фоновом режиме",
|
||||||
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
|
"xWasPossiblyUpdatedToY": "{} возможно был обновлен до {}",
|
||||||
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
|
"enableBackgroundUpdates": "Включить обновления в фоне",
|
||||||
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
|
"backgroundUpdateReqsExplanation": "Фоновые обновления могут быть возможны не для всех приложений",
|
||||||
"verifyLatestTag": "Verify the 'latest' tag",
|
"backgroundUpdateLimitsExplanation": "Успешность фоновой установки можно определить только после открытия Obtainium",
|
||||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
"verifyLatestTag": "Проверять тег 'latest'",
|
||||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
|
"intermediateLinkRegex": "Фильтр для \"промежуточной\" ссылки для посещения",
|
||||||
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
"filterByLinkText": "Фильтрация ссылок по тексту ссылки",
|
||||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
"intermediateLinkNotFound": "Промежуточная ссылка не найдена",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"intermediateLink": "Промежуточное звено",
|
||||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
"exemptFromBackgroundUpdates": "Исключить из фоновых обновлений (если включено)",
|
||||||
"pickExportDir": "Pick Export Directory",
|
"bgUpdatesOnWiFiOnly": "Отключить фоновые обновления, если нет соединения с Wi-Fi",
|
||||||
"autoExportOnChanges": "Auto-export on changes",
|
"autoSelectHighestVersionCode": "Автоматически выбирать APK с актуальной версией кода",
|
||||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
"versionExtractionRegEx": "Регулярное выражение для извлечения версии",
|
||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"matchGroupToUse": "Выберите группу для использования",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"highlightTouchTargets": "Выделить менее очевидные элементы управления касанием",
|
||||||
"reverseSort": "Reverse sorting",
|
"pickExportDir": "Выбрать каталог для экспорта",
|
||||||
"debugMenu": "Debug Menu",
|
"autoExportOnChanges": "Автоэкспорт при изменениях",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"includeSettings": "Включить настройки",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"filterVersionsByRegEx": "Фильтровать версии по регулярному выражению",
|
||||||
|
"trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
|
||||||
|
"dontSortReleasesList": "Сохранить порядок релизов от API",
|
||||||
|
"reverseSort": "Обратная сортировка",
|
||||||
|
"takeFirstLink": "Взять первую ссылку",
|
||||||
|
"skipSort": "Пропустить сортировку",
|
||||||
|
"debugMenu": "Меню отладки",
|
||||||
|
"bgTaskStarted": "Фоновая задача начата — проверьте журналы",
|
||||||
|
"runBgCheckNow": "Запустить проверку фонового обновления сейчас",
|
||||||
|
"versionExtractWholePage": "Применить регулярное выражение версии ко всей странице",
|
||||||
|
"installing": "Устанавливается",
|
||||||
|
"skipUpdateNotifications": "Не оповещать об обновлениях",
|
||||||
|
"updatesAvailableNotifChannel": "Доступны обновления",
|
||||||
|
"appsUpdatedNotifChannel": "Приложения обновлены",
|
||||||
|
"appsPossiblyUpdatedNotifChannel": "Попытки обновления приложений",
|
||||||
|
"errorCheckingUpdatesNotifChannel": "Ошибка при проверке обновлений",
|
||||||
|
"appsRemovedNotifChannel": "Приложение удалено",
|
||||||
|
"downloadingXNotifChannel": "Загрузка {}",
|
||||||
|
"completeAppInstallationNotifChannel": "Завершение установки приложения",
|
||||||
|
"checkingForUpdatesNotifChannel": "Проверка обновлений",
|
||||||
|
"onlyCheckInstalledOrTrackOnlyApps": "Проверять обновления только у установленных или отслеживаемых приложений",
|
||||||
|
"supportFixedAPKURL": "Поддержка фиксированных URL-адресов APK",
|
||||||
|
"selectX": "Выбрать {}",
|
||||||
|
"parallelDownloads": "Разрешить параллельные загрузки",
|
||||||
|
"installMethod": "Метод установки",
|
||||||
|
"normal": "Нормальный",
|
||||||
|
"root": "Суперпользователь",
|
||||||
|
"shizukuBinderNotFound": "Совместимый сервис Shizuku не найден",
|
||||||
|
"useSystemFont": "Использовать системный шрифт",
|
||||||
|
"systemFontError": "Ошибка загрузки системного шрифта: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Использовать код версии приложения как версию, обнаруженную ОС",
|
||||||
|
"requestHeader": "Заголовок запроса",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Использовать последнюю загрузку ресурса в качестве даты выпуска",
|
||||||
|
"defaultPseudoVersioningMethod": "Метод псевдоверсионирования по умолчанию",
|
||||||
|
"partialAPKHash": "Частичный хэш APK",
|
||||||
|
"APKLinkHash": "Хеш ссылки APK",
|
||||||
|
"directAPKLink": "Прямая ссылка на APK",
|
||||||
|
"pseudoVersionInUse": "Используется псевдоверсия",
|
||||||
|
"installed": "Установлен",
|
||||||
|
"latest": "Последний",
|
||||||
|
"invertRegEx": "Инвертировать регулярное выражение",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Удалить приложение?",
|
"one": "Удалить приложение?",
|
||||||
"other": "Удалить приложения?"
|
"other": "Удалить приложения?"
|
||||||
},
|
},
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "Слишком много запросов (ограничение скорости) - попробуйте снова через {} минуту",
|
"one": "Слишком много запросов (ограничение скорости) — попробуйте снова через {} минуту",
|
||||||
"other": "Слишком много запросов (ограничение скорости) - попробуйте снова через {} минуты"
|
"other": "Слишком много запросов (ограничение скорости) — попробуйте снова через {} минуты"
|
||||||
},
|
},
|
||||||
"bgUpdateGotErrorRetryInMinutes": {
|
"bgUpdateGotErrorRetryInMinutes": {
|
||||||
"one": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет запланирована через {} минуту",
|
"one": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет запланирована через {} минуту",
|
||||||
"other": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет запланирована через {} минуты"
|
"other": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет запланирована через {} минуты"
|
||||||
},
|
},
|
||||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||||
"one": "В ходе проверки обновления в фоновом режиме было обнаружено {} обновление - Пользователю будет отправлено уведомление, если это необходимо",
|
"one": "В ходе проверки обновления в фоновом режиме было обнаружено {} обновление — Пользователю будет отправлено уведомление, если это необходимо",
|
||||||
"other": "В ходе проверки обновления в фоновом режиме было обнаружено {} обновлений - Пользователю будет отправлено уведомление, если это необходимо"
|
"other": "В ходе проверки обновления в фоновом режиме было обнаружено {} обновлений — Пользователю будет отправлено уведомление, если это необходимо"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"one": "{} Приложение",
|
"one": "{} приложение",
|
||||||
"other": "{} Приложений"
|
"other": "{} приложений"
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"one": "{} URL-адрес",
|
"one": "{} URL-адрес",
|
||||||
"other": "{} URL-адреса"
|
"other": "{} URL-адреса"
|
||||||
},
|
},
|
||||||
"minute": {
|
"minute": {
|
||||||
"one": "{} Минута",
|
"one": "{} минута",
|
||||||
"other": "{} Минуты"
|
"other": "{} минуты"
|
||||||
},
|
},
|
||||||
"hour": {
|
"hour": {
|
||||||
"one": "{} Час",
|
"one": "{} час",
|
||||||
"other": "{} Часов"
|
"other": "{} часов"
|
||||||
},
|
},
|
||||||
"day": {
|
"day": {
|
||||||
"one": "{} День",
|
"one": "{} день",
|
||||||
"other": "{} Дней"
|
"other": "{} дней"
|
||||||
},
|
},
|
||||||
"clearedNLogsBeforeXAfterY": {
|
"clearedNLogsBeforeXAfterY": {
|
||||||
"one": "Очищен {n} журнал (до = {before}, после = {after})",
|
"one": "Очищен {n} журнал (до = {before}, после = {after})",
|
||||||
"other": "Очищено {n} журналов (до = {before}, после = {after})"
|
"other": "Очищено {n} журналов (до = {before}, после = {after})"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesAvailable": {
|
"xAndNMoreUpdatesAvailable": {
|
||||||
"one": "У {} и еще 1 приложения есть обновление.",
|
"one": "У {} и еще 1 приложения есть обновление",
|
||||||
"other": "У {} и ещё {} приложений есть обновления."
|
"other": "У {} и ещё {} приложений есть обновления"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesInstalled": {
|
"xAndNMoreUpdatesInstalled": {
|
||||||
"one": "{} и еще 1 приложение были обновлены.",
|
"one": "{} и ещё 1 приложение были обновлены",
|
||||||
"other": "{} и еще {} приложений были обновлены."
|
"other": "{} и ещё {} приложений были обновлены"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
"one": "{} and 1 more app may have been updated.",
|
"one": "{} и ещё 1 приложение могли быть обновлены",
|
||||||
"other": "{} and {} more apps may have been updated."
|
"other": "{} и ещё {} приложений могли быть обновлены"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
assets/translations/standardize.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Take one (hardcoded) translation file and ensure that all other translation files have the same keys in the same order
|
||||||
|
// Then report which other translation files have identical items
|
||||||
|
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
const translationsDir = __dirname
|
||||||
|
const templateFile = `${translationsDir}/en.json`
|
||||||
|
const otherFiles = fs.readdirSync(translationsDir).map(f => {
|
||||||
|
return `${translationsDir}/${f}`
|
||||||
|
}).filter(f => f.endsWith('.json') && f != templateFile)
|
||||||
|
|
||||||
|
const templateTranslation = require(templateFile)
|
||||||
|
|
||||||
|
otherFiles.forEach(file => {
|
||||||
|
const thisTranslationOriginal = require(file)
|
||||||
|
const thisTranslationNew = {}
|
||||||
|
Object.keys(templateTranslation).forEach(k => {
|
||||||
|
thisTranslationNew[k] = thisTranslationOriginal[k] || templateTranslation[k]
|
||||||
|
})
|
||||||
|
fs.writeFileSync(file, `${JSON.stringify(thisTranslationNew, null, ' ')}\n`)
|
||||||
|
});
|
||||||
|
|
||||||
|
otherFiles.forEach(file => {
|
||||||
|
const thisTranslation = require(file)
|
||||||
|
Object.keys(templateTranslation).forEach(k => {
|
||||||
|
if (JSON.stringify(thisTranslation[k]) == JSON.stringify(templateTranslation[k])) {
|
||||||
|
console.log(`${file} :::: ${k} :::: ${JSON.stringify(thisTranslation[k])}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
355
assets/translations/sv.json
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
{
|
||||||
|
"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": "Den här klassen har inte implementerat den här funktionen",
|
||||||
|
"placeholder": "Platshållare",
|
||||||
|
"someErrors": "Några fel uppstod",
|
||||||
|
"unexpectedError": "Oväntat fel",
|
||||||
|
"ok": "Okej",
|
||||||
|
"and": "och",
|
||||||
|
"githubPATLabel": "GitHub Personal Access Token (ökar hastighetsgräns)",
|
||||||
|
"includePrereleases": "Inkludera förreleaser",
|
||||||
|
"fallbackToOlderReleases": "Fall tillbaka till äldre releaser",
|
||||||
|
"filterReleaseTitlesByRegEx": "Filtrera utgivningstitlar efter reguljärt uttryck",
|
||||||
|
"invalidRegEx": "Ogiltigt reguljärt uttryck",
|
||||||
|
"noDescription": "Ingen beskrivning",
|
||||||
|
"cancel": "Avbryt",
|
||||||
|
"continue": "Fortsätt",
|
||||||
|
"requiredInBrackets": "(Kräver)",
|
||||||
|
"dropdownNoOptsError": "FEL: DROPDOWN MÅSTE HA MINST ETT OPT",
|
||||||
|
"colour": "Färg",
|
||||||
|
"githubStarredRepos": "GitHub Stjärnmärkta Förråd",
|
||||||
|
"uname": "Användarnamn",
|
||||||
|
"wrongArgNum": "Fel antal argument har angetts",
|
||||||
|
"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": "Framsteg: {}%",
|
||||||
|
"pleaseWait": "Vänta",
|
||||||
|
"updateAvailable": "Uppdatering Tillgänglig",
|
||||||
|
"notInstalled": "Inte Installerad",
|
||||||
|
"pseudoVersion": "pseudoversion",
|
||||||
|
"selectAll": "Välj Alla",
|
||||||
|
"deselectX": "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.",
|
||||||
|
"customLinkMessage": "Dessa länkar fungerar på enheter med Obtainium installerat",
|
||||||
|
"shareAppConfigLinks": "Dela appkonfiguration som HTML-länk",
|
||||||
|
"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": "Filtrera",
|
||||||
|
"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-lista",
|
||||||
|
"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:",
|
||||||
|
"selectURL": "Välj URL",
|
||||||
|
"selectURLs": "Välj URL:er",
|
||||||
|
"pick": "Välj",
|
||||||
|
"theme": "Tema",
|
||||||
|
"dark": "Mörkt",
|
||||||
|
"light": "Ljust",
|
||||||
|
"followSystem": "Följ System",
|
||||||
|
"useBlackTheme": "Använd svart tema",
|
||||||
|
"appSortBy": "Sortera Appar via",
|
||||||
|
"authorName": "Utvecklare/Namn",
|
||||||
|
"nameAuthor": "Namn/Utvecklare",
|
||||||
|
"asAdded": "Som tillagd",
|
||||||
|
"appSortOrder": "Appsorteringsordning",
|
||||||
|
"ascending": "Stigande",
|
||||||
|
"descending": "Fallande",
|
||||||
|
"bgUpdateCheckInterval": "Bakgrundsuppdateringskollfrekvens",
|
||||||
|
"neverManualOnly": "Aldrig - Endast manuell",
|
||||||
|
"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": "Meddelar användaren att uppdateringar av en eller flera appar har tillämpats i bakgrunden",
|
||||||
|
"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": "Övergående meddelande som visas när du söker efter uppdateringar",
|
||||||
|
"pleaseAllowInstallPerm": "Tillåt Obtanium att installera Appar",
|
||||||
|
"trackOnly": "Följ-Endast",
|
||||||
|
"errorWithHttpStatusCode": "Fel {}",
|
||||||
|
"versionCorrectionDisabled": "Versionskorrigering inaktiverat (plugin verkar inte fungera)",
|
||||||
|
"unknown": "Okänd",
|
||||||
|
"none": "Ingen",
|
||||||
|
"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",
|
||||||
|
"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": "Det här alternativet bör endast användas för appar där versionsidentifiering inte fungerar korrekt.",
|
||||||
|
"downloadingX": "Laddar ner {}",
|
||||||
|
"downloadNotifDescription": "Meddelar användaren om framstegen med att ladda ner en 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": "Märka",
|
||||||
|
"language": "Språk",
|
||||||
|
"copiedToClipboard": "Kopierat till Urklipp",
|
||||||
|
"storagePermissionDenied": "Lagringsbehörighet nekad",
|
||||||
|
"selectedCategorizeWarning": "Detta kommer att ersätta alla befintliga kategoriinställningar för de valda apparna.",
|
||||||
|
"filterAPKsByRegEx": "Filtrera APK-filer efter reguljärt uttryck",
|
||||||
|
"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": "Det här alternativet bör endast användas för appar där versionsidentifiering inte fungerar korrekt, men ett releasedatum är tillgängligt.",
|
||||||
|
"changes": "Ändringar",
|
||||||
|
"releaseDate": "Releasedatum",
|
||||||
|
"importFromURLsInFile": "Importera från URL:er i fil (som OPML)",
|
||||||
|
"versionDetectionExplanation": "Sammanställ versionssträng med version upptäckt från OS",
|
||||||
|
"versionDetection": "Versionsdetektering",
|
||||||
|
"standardVersionDetection": "Standardversionsdetektering",
|
||||||
|
"groupByCategory": "Gruppera via Kategori",
|
||||||
|
"autoApkFilterByArch": "Försök att filtrera APK-filer efter CPU-arkitektur om möjligt",
|
||||||
|
"overrideSource": "Överskrid Källa",
|
||||||
|
"dontShowAgain": "Visa inte detta igen",
|
||||||
|
"dontShowTrackOnlyWarnings": "Visa inte 'Följ-Endast' varningar",
|
||||||
|
"dontShowAPKOriginWarnings": "Visa inte APK-ursprung varningar",
|
||||||
|
"moveNonInstalledAppsToBottom": "Flytta icke-installerade appar till botten av appvyn",
|
||||||
|
"gitlabPATLabel": "GitLab Personal Access Token\\n(Möjliggör sökning och bättre APK-upptäckt)",
|
||||||
|
"about": "Om",
|
||||||
|
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
|
||||||
|
"checkOnStart": "Kolla efter uppdateringar vid start",
|
||||||
|
"tryInferAppIdFromCode": "Försök härleda app-id från källkoden",
|
||||||
|
"removeOnExternalUninstall": "Ta bort externt avinstallerade appar automatiskt",
|
||||||
|
"pickHighestVersionCode": "Välj automatiskt högsta versionskod APK",
|
||||||
|
"checkUpdateOnDetailPage": "Sök efter uppdateringar om att öppna en app-detaljsida",
|
||||||
|
"disablePageTransitions": "Inaktivera sidövergångsanimationer",
|
||||||
|
"reversePageTransitions": "Animeringar för omvänd sidövergång",
|
||||||
|
"minStarCount": "Minsta antal stjärnmarkeringar",
|
||||||
|
"addInfoBelow": "Lägg till denna information nedanför.",
|
||||||
|
"addInfoInSettings": "Lägg till denna information i Inställningar.",
|
||||||
|
"githubSourceNote": "GitHub-hastighetsbegränsning kan undvikas med en API-nyckel.",
|
||||||
|
"gitlabSourceNote": "GitLab APK-extraktion kanske inte fungerar utan en API-nyckel.",
|
||||||
|
"sortByLastLinkSegment": "Sortera endast efter det sista segmentet av länken",
|
||||||
|
"filterReleaseNotesByRegEx": "Filtrera versionskommentarer efter reguljärt uttryck",
|
||||||
|
"customLinkFilterRegex": "Anpassad APK-länkfiltrera efter reguljärt uttryck (standard '.apk$')",
|
||||||
|
"appsPossiblyUpdated": "Appuppdateringar försökte",
|
||||||
|
"appsPossiblyUpdatedNotifDescription": "Meddelar användaren om att uppdateringar av en eller flera appar potentiellt har tillämpats i bakgrunden",
|
||||||
|
"xWasPossiblyUpdatedToY": "{} kan ha uppdaterats till {}.",
|
||||||
|
"enableBackgroundUpdates": "Aktivera Bakgrundsuppdateringar",
|
||||||
|
"backgroundUpdateReqsExplanation": "Bakgrundsuppdateringar är inte möjligt för alla appar.",
|
||||||
|
"backgroundUpdateLimitsExplanation": "Framgången för en bakgrundsinstallation kan bara avgöras när Obtainium öppnas.",
|
||||||
|
"verifyLatestTag": "Verifiera 'senaste'-taggen",
|
||||||
|
"intermediateLinkRegex": "Filtrera för en \"mellanliggande\" länk att besöka",
|
||||||
|
"filterByLinkText": "Filtrera länkar efter länktext",
|
||||||
|
"intermediateLinkNotFound": "Mellanlänk hittades inte",
|
||||||
|
"intermediateLink": "Mellanlänk",
|
||||||
|
"exemptFromBackgroundUpdates": "Undta från bakgrundsuppdateringar (om aktiverad)",
|
||||||
|
"bgUpdatesOnWiFiOnly": "Inaktivera Bakgrundsuppdateringar utan WiFi",
|
||||||
|
"autoSelectHighestVersionCode": "Välj automatiskt högsta versionskod APK",
|
||||||
|
"versionExtractionRegEx": "Version Extraction RegEx",
|
||||||
|
"matchGroupToUse": "Match Group to Use",
|
||||||
|
"highlightTouchTargets": "Markera mindre uppenbara beröringsobjekt",
|
||||||
|
"pickExportDir": "Välj Exportsökväg",
|
||||||
|
"autoExportOnChanges": "Automatisk export vid ändringar",
|
||||||
|
"includeSettings": "Inkludera inställningar",
|
||||||
|
"filterVersionsByRegEx": "Filtrera versioner efter reguljärt uttryck",
|
||||||
|
"trySelectingSuggestedVersionCode": "Försök att välja föreslagen versionCode APK",
|
||||||
|
"dontSortReleasesList": "Behåll släpporder från API",
|
||||||
|
"reverseSort": "Omvänd sortering",
|
||||||
|
"takeFirstLink": "Ta första länken",
|
||||||
|
"skipSort": "Hoppa över sortering",
|
||||||
|
"debugMenu": "Felsökningsmeny",
|
||||||
|
"bgTaskStarted": "Bakgrundsuppgift startat - kontrollera loggar.",
|
||||||
|
"runBgCheckNow": "Kör Bakgrundsuppdateringskoll Nu",
|
||||||
|
"versionExtractWholePage": "Tillämpa Regex för versionssträngextraktion på hela sidan",
|
||||||
|
"installing": "Installerar",
|
||||||
|
"skipUpdateNotifications": "Hoppa över uppdateringsmeddelanden",
|
||||||
|
"updatesAvailableNotifChannel": "Uppdateringar tillgängliga",
|
||||||
|
"appsUpdatedNotifChannel": "Appar uppdaterade",
|
||||||
|
"appsPossiblyUpdatedNotifChannel": "Appuppdateringar försökte",
|
||||||
|
"errorCheckingUpdatesNotifChannel": "Fel vid sökning efter uppdateringar",
|
||||||
|
"appsRemovedNotifChannel": "Appar borttagna",
|
||||||
|
"downloadingXNotifChannel": "Laddar ner {}",
|
||||||
|
"completeAppInstallationNotifChannel": "Fullständig appinstallation",
|
||||||
|
"checkingForUpdatesNotifChannel": "Kollar efter uppdateringar",
|
||||||
|
"onlyCheckInstalledOrTrackOnlyApps": "Kontrollera bara installerade appar och endast spårbara appar för uppdateringar",
|
||||||
|
"supportFixedAPKURL": "Stöd fasta APK-webbadresser",
|
||||||
|
"selectX": "Välj {}",
|
||||||
|
"parallelDownloads": "Tillåt parallella nedladdningar",
|
||||||
|
"installMethod": "Installationsmetod",
|
||||||
|
"normal": "Vanligt",
|
||||||
|
"root": "Rot",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
|
"useSystemFont": "Använd systemteckensnittet",
|
||||||
|
"systemFontError": "Fel vid laddning av systemteckensnittet: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Använd appversionskoden som OS-upptäckt version",
|
||||||
|
"requestHeader": "Request header",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Använd senaste tillgångsuppladdning som releasedatum",
|
||||||
|
"defaultPseudoVersioningMethod": "Standard pseudoversionsmetod",
|
||||||
|
"partialAPKHash": "Delvis APK-hash",
|
||||||
|
"APKLinkHash": "APK Link Hash",
|
||||||
|
"directAPKLink": "Direkt APK-länk",
|
||||||
|
"pseudoVersionInUse": "En pseudoversion används",
|
||||||
|
"installed": "Installerad",
|
||||||
|
"latest": "Senast",
|
||||||
|
"invertRegEx": "Invertera reguljärt uttryck",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
|
"removeAppQuestion": {
|
||||||
|
"one": "Ta Bort App?",
|
||||||
|
"other": "Ta Bort Appar?"
|
||||||
|
},
|
||||||
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
|
"one": "För många förfrågningar (begränsat pris) - försök igen om {} minut",
|
||||||
|
"other": "För många förfrågningar (begränsat pris) - försök igen om {} minuter"
|
||||||
|
},
|
||||||
|
"bgUpdateGotErrorRetryInMinutes": {
|
||||||
|
"one": "BG-uppdateringskontroll påträffade en {}, kommer att schemalägga ett nytt försök om {} minut",
|
||||||
|
"other": "BG-uppdateringskontroll påträffade en {}, kommer att schemalägga ett nytt försök om {} minuter"
|
||||||
|
},
|
||||||
|
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||||
|
"one": "BG-uppdateringskontroll hittades {} uppdatering - kommer att meddela användaren om det behövs",
|
||||||
|
"other": "BG-uppdateringskontroll hittades {} uppdateringar - kommer att meddela användaren om det behövs"
|
||||||
|
},
|
||||||
|
"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."
|
||||||
|
}
|
||||||
|
}
|
||||||
355
assets/translations/tr.json
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
{
|
||||||
|
"invalidURLForSource": "Geçerli bir {} Uygulama URL'si değil",
|
||||||
|
"noReleaseFound": "Uygun bir sürüm bulunamadı",
|
||||||
|
"noVersionFound": "Sürüm bulunamadı",
|
||||||
|
"urlMatchesNoSource": "URL, bilinen bir kaynağa uymuyor",
|
||||||
|
"cantInstallOlderVersion": "Eski bir sürümü yükleyemem",
|
||||||
|
"appIdMismatch": "İndirilen paket kimliği mevcut Uygulama kimliği ile eşleşmiyor",
|
||||||
|
"functionNotImplemented": "Bu sınıf bu işlevi uygulamamıştır",
|
||||||
|
"placeholder": "Yer Tutucu",
|
||||||
|
"someErrors": "Bazı Hatalar Oluştu",
|
||||||
|
"unexpectedError": "Beklenmeyen Hata",
|
||||||
|
"ok": "Tamam",
|
||||||
|
"and": "ve",
|
||||||
|
"githubPATLabel": "GitHub Kişisel Erişim Anahtarı (Sınırlamayı Artırır)",
|
||||||
|
"includePrereleases": "Ön sürümleri dahil et",
|
||||||
|
"fallbackToOlderReleases": "Daha eski sürümlere geri dön",
|
||||||
|
"filterReleaseTitlesByRegEx": "Düzenli İfadelerle Sürüm Başlıklarını Filtrele",
|
||||||
|
"invalidRegEx": "Geçersiz düzenli ifade",
|
||||||
|
"noDescription": "Açıklama yok",
|
||||||
|
"cancel": "İptal",
|
||||||
|
"continue": "Devam Et",
|
||||||
|
"requiredInBrackets": "(Gerekli)",
|
||||||
|
"dropdownNoOptsError": "HATA: DİPLOMADA EN AZ BİR SEÇENEK OLMALI",
|
||||||
|
"colour": "Renk",
|
||||||
|
"githubStarredRepos": "GitHub'a Yıldızlı Depolar",
|
||||||
|
"uname": "Kullanıcı Adı",
|
||||||
|
"wrongArgNum": "Hatalı argüman sayısı sağlandı",
|
||||||
|
"xIsTrackOnly": "{} yalnızca Takip Edilen",
|
||||||
|
"source": "Kaynak",
|
||||||
|
"app": "Uygulama",
|
||||||
|
"appsFromSourceAreTrackOnly": "Bu kaynaktan gelen uygulamalar 'Yalnızca Takip Edilen'dir.",
|
||||||
|
"youPickedTrackOnly": "'Yalnızca Takip Edilen' seçeneğini seçtiniz.",
|
||||||
|
"trackOnlyAppDescription": "Uygulama güncellemeleri için takip edilecek, ancak Obtainium onu indiremeyecek veya kuramayacaktır.",
|
||||||
|
"cancelled": "İptal Edildi",
|
||||||
|
"appAlreadyAdded": "Uygulama zaten eklenmiş",
|
||||||
|
"alreadyUpToDateQuestion": "Uygulama Zaten Güncel mi?",
|
||||||
|
"addApp": "Uygulama Ekle",
|
||||||
|
"appSourceURL": "Uygulama Kaynak URL'si",
|
||||||
|
"error": "Hata",
|
||||||
|
"add": "Ekle",
|
||||||
|
"searchSomeSourcesLabel": "Ara (Bazı Kaynaklar Yalnızca)",
|
||||||
|
"search": "Ara",
|
||||||
|
"additionalOptsFor": "{} İçin Ek Seçenekler",
|
||||||
|
"supportedSources": "Desteklenen Kaynaklar",
|
||||||
|
"trackOnlyInBrackets": "(Yalnızca Takip)",
|
||||||
|
"searchableInBrackets": "(Aranabilir)",
|
||||||
|
"appsString": "Uygulamalar",
|
||||||
|
"noApps": "Uygulama Yok",
|
||||||
|
"noAppsForFilter": "Filtre İçin Uygulama Yok",
|
||||||
|
"byX": "{} Tarafından",
|
||||||
|
"percentProgress": "İlerleme: {}%",
|
||||||
|
"pleaseWait": "Lütfen Bekleyin",
|
||||||
|
"updateAvailable": "Güncelleme Var",
|
||||||
|
"notInstalled": "Yüklenmedi",
|
||||||
|
"pseudoVersion": "sözde versiyon",
|
||||||
|
"selectAll": "Hepsini Seç",
|
||||||
|
"deselectX": "{}'yi Seçimden Kaldır",
|
||||||
|
"xWillBeRemovedButRemainInstalled": "{} Obtainium'dan kaldırılacak ancak cihazınızda yüklü kalacaktır.",
|
||||||
|
"removeSelectedAppsQuestion": "Seçilen Uygulamaları Kaldırmak İstiyor musunuz?",
|
||||||
|
"removeSelectedApps": "Seçilen Uygulamaları Kaldır",
|
||||||
|
"updateX": "{}'yi Güncelle",
|
||||||
|
"installX": "{}'yi Yükle",
|
||||||
|
"markXTrackOnlyAsUpdated": "{}(Takip Edilen) olarak Güncellendi olarak İşaretle",
|
||||||
|
"changeX": "{}'yi Değiştir",
|
||||||
|
"installUpdateApps": "Uygulamaları Yükle/Güncelle",
|
||||||
|
"installUpdateSelectedApps": "Seçilen Uygulamaları Yükle/Güncelle",
|
||||||
|
"markXSelectedAppsAsUpdated": "Seçilen Uygulamaları {} olarak Güncellendi olarak İşaretle?",
|
||||||
|
"no": "Hayır",
|
||||||
|
"yes": "Evet",
|
||||||
|
"markSelectedAppsUpdated": "Seçilen Uygulamaları Güncellendi olarak İşaretle",
|
||||||
|
"pinToTop": "Üstte Tut",
|
||||||
|
"unpinFromTop": "Üstten Kaldır",
|
||||||
|
"resetInstallStatusForSelectedAppsQuestion": "Seçilen Uygulamaların Yükleme Durumunu Sıfırlamak İstiyor musunuz?",
|
||||||
|
"installStatusOfXWillBeResetExplanation": "Seçilen Uygulamaların yükleme durumu sıfırlanacak.\n\nBu, Obtainium'da gösterilen uygulama sürümünün başarısız güncellemeler veya diğer sorunlar nedeniyle yanlış olması durumunda yardımcı olabilir.",
|
||||||
|
"customLinkMessage": "Bu bağlantılar Obtainium'un kurulu olduğu cihazlarda çalışır",
|
||||||
|
"shareAppConfigLinks": "Uygulama yapılandırmasını HTML bağlantısı olarak paylaş",
|
||||||
|
"shareSelectedAppURLs": "Seçilen Uygulama URL'larını Paylaş",
|
||||||
|
"resetInstallStatus": "Yükleme Durumunu Sıfırla",
|
||||||
|
"more": "Daha Fazla",
|
||||||
|
"removeOutdatedFilter": "Güncel Olmayan Uygulama Filtresini Kaldır",
|
||||||
|
"showOutdatedOnly": "Yalnızca Güncel Olmayan Uygulamaları Göster",
|
||||||
|
"filter": "Filtre",
|
||||||
|
"filterActive": "Filtre *",
|
||||||
|
"filterApps": "Uygulamaları Filtrele",
|
||||||
|
"appName": "Uygulama Adı",
|
||||||
|
"author": "Yazar",
|
||||||
|
"upToDateApps": "Güncel Uygulamalar",
|
||||||
|
"nonInstalledApps": "Yüklenmemiş Uygulamalar",
|
||||||
|
"importExport": "İçe/Dışa Aktar",
|
||||||
|
"settings": "Ayarlar",
|
||||||
|
"exportedTo": "{}'e Dışa Aktarıldı",
|
||||||
|
"obtainiumExport": "Obtainium Dışa Aktar",
|
||||||
|
"invalidInput": "Geçersiz Giriş",
|
||||||
|
"importedX": "{} İçe Aktarıldı",
|
||||||
|
"obtainiumImport": "Obtainium İçe Aktar",
|
||||||
|
"importFromURLList": "URL Listesinden İçe Aktar (Örneğin OPML)",
|
||||||
|
"searchQuery": "Arama Sorgusu",
|
||||||
|
"appURLList": "Uygulama URL Listesi",
|
||||||
|
"line": "Satır",
|
||||||
|
"searchX": "{}'yi Ara",
|
||||||
|
"noResults": "Sonuç Bulunamadı",
|
||||||
|
"importX": "{} İçe Aktar",
|
||||||
|
"importedAppsIdDisclaimer": "İçe Aktarılan Uygulamalar yanlışlıkla \"Yüklenmedi\" olarak gösterilebilir.\nBunu düzeltmek için bunları Obtainium üzerinden yeniden yükleyin.\nBu, yalnızca URL ve üçüncü taraf içe aktarma yöntemlerini etkiler.\n\nYalnızca URL ve üçüncü taraf içe aktarma yöntemlerini etkiler.",
|
||||||
|
"importErrors": "İçe Aktarma Hataları",
|
||||||
|
"importedXOfYApps": "{}'den {} Uygulama İçe Aktarıldı.",
|
||||||
|
"followingURLsHadErrors": "Aşağıdaki URL'lerde hatalar oluştu:",
|
||||||
|
"selectURL": "URL Seç",
|
||||||
|
"selectURLs": "URL'leri Seç",
|
||||||
|
"pick": "Seç",
|
||||||
|
"theme": "Tema",
|
||||||
|
"dark": "Koyu",
|
||||||
|
"light": "Aydınlık",
|
||||||
|
"followSystem": "Sistemi Takip Et",
|
||||||
|
"useBlackTheme": "Saf siyah koyu temasını kullan",
|
||||||
|
"appSortBy": "Uygulama Sıralama Ölçütü",
|
||||||
|
"authorName": "Yazar/Ad",
|
||||||
|
"nameAuthor": "Ad/Yazar",
|
||||||
|
"asAdded": "Eklendiği Gibi",
|
||||||
|
"appSortOrder": "Uygulama Sıralama Sırası",
|
||||||
|
"ascending": "Artan",
|
||||||
|
"descending": "Azalan",
|
||||||
|
"bgUpdateCheckInterval": "Arka Planda Güncelleme Kontrol Aralığı",
|
||||||
|
"neverManualOnly": "Asla - Yalnızca El ile",
|
||||||
|
"appearance": "Görünüm",
|
||||||
|
"showWebInAppView": "Kaynağı Uygulama Görünümünde Göster",
|
||||||
|
"pinUpdates": "Güncellemeleri Uygulamalar Görünümünün Üstüne Sabitle",
|
||||||
|
"updates": "Güncellemeler",
|
||||||
|
"sourceSpecific": "Kaynak Özel",
|
||||||
|
"appSource": "Uygulama Kaynağı",
|
||||||
|
"noLogs": "Günlük Yok",
|
||||||
|
"appLogs": "Uygulama Günlükleri",
|
||||||
|
"close": "Kapat",
|
||||||
|
"share": "Paylaş",
|
||||||
|
"appNotFound": "Uygulama Bulunamadı",
|
||||||
|
"obtainiumExportHyphenatedLowercase": "obtainium-ihracat",
|
||||||
|
"pickAnAPK": "APK Seç",
|
||||||
|
"appHasMoreThanOnePackage": "{}'nin birden fazla paketi var:",
|
||||||
|
"deviceSupportsXArch": "Cihazınız {} CPU mimarisini destekliyor.",
|
||||||
|
"deviceSupportsFollowingArchs": "Cihazınız aşağıdaki CPU mimarilerini destekliyor:",
|
||||||
|
"warning": "Uyarı",
|
||||||
|
"sourceIsXButPackageFromYPrompt": "Uygulama kaynağı '{}', ancak dağıtım paketi '{}'. Devam etmek istiyor musunuz?",
|
||||||
|
"updatesAvailable": "Güncellemeler Var",
|
||||||
|
"updatesAvailableNotifDescription": "Kullanıcıya Obtainium tarafından takip edilen bir veya daha fazla uygulama için güncelleme bulunduğuna dair bilgi verir",
|
||||||
|
"noNewUpdates": "Yeni güncelleme yok.",
|
||||||
|
"xHasAnUpdate": "{} güncelleme alıyor.",
|
||||||
|
"appsUpdated": "Uygulamalar Güncellendi",
|
||||||
|
"appsUpdatedNotifDescription": "Kullanıcıya bir veya daha fazla uygulamanın arka planda güncellendiğine dair bilgi verir",
|
||||||
|
"xWasUpdatedToY": "{} şu sürüme güncellendi: {}.",
|
||||||
|
"errorCheckingUpdates": "Güncellemeler Kontrol Edilirken Hata Oluştu",
|
||||||
|
"errorCheckingUpdatesNotifDescription": "Arka planda güncelleme kontrolü sırasında hata oluştuğunda görünen bir bildirim",
|
||||||
|
"appsRemoved": "Uygulamalar Kaldırıldı",
|
||||||
|
"appsRemovedNotifDescription": "Bir veya daha fazla uygulamanın yüklenirken hata nedeniyle kaldırıldığını bildiren bir bildirim",
|
||||||
|
"xWasRemovedDueToErrorY": "{} şu hatadan dolayı kaldırıldı: {}",
|
||||||
|
"completeAppInstallation": "Uygulama Yüklemeyi Tamamla",
|
||||||
|
"obtainiumMustBeOpenToInstallApps": "Uygulamaları yüklemek için Obtainium'un açık olması gerekiyor",
|
||||||
|
"completeAppInstallationNotifDescription": "Kullanıcıdan Obtainium'a geri dönüp bir uygulama yüklemeyi tamamlamasını isteyen bir bildirim",
|
||||||
|
"checkingForUpdates": "Güncellemeler Kontrol Ediliyor",
|
||||||
|
"checkingForUpdatesNotifDescription": "Güncellemeler kontrol edildiğinde görünen geçici bir bildirim",
|
||||||
|
"pleaseAllowInstallPerm": "Lütfen Obtainium'un uygulama yüklemesine izin verin",
|
||||||
|
"trackOnly": "Sadece Takip Et",
|
||||||
|
"errorWithHttpStatusCode": "Hata {}",
|
||||||
|
"versionCorrectionDisabled": "Sürüm düzeltme devre dışı bırakıldı (eklenti çalışmıyor gibi görünüyor)",
|
||||||
|
"unknown": "Bilinmiyor",
|
||||||
|
"none": "Hiçbiri",
|
||||||
|
"never": "Asla",
|
||||||
|
"latestVersionX": "En Son Sürüm: {}",
|
||||||
|
"installedVersionX": "Yüklenen Sürüm: {}",
|
||||||
|
"lastUpdateCheckX": "Son Güncelleme Kontrolü: {}",
|
||||||
|
"remove": "Kaldır",
|
||||||
|
"yesMarkUpdated": "Evet, Güncellendi olarak İşaretle",
|
||||||
|
"fdroid": "F-Droid Resmi",
|
||||||
|
"appIdOrName": "Uygulama Kimliği veya Adı",
|
||||||
|
"appId": "Uygulama Kimliği",
|
||||||
|
"appWithIdOrNameNotFound": "Bu kimlik veya ada sahip bir uygulama bulunamadı",
|
||||||
|
"reposHaveMultipleApps": "Depolar birden fazla uygulama içerebilir",
|
||||||
|
"fdroidThirdPartyRepo": "F-Droid Üçüncü Taraf Depo",
|
||||||
|
"steamMobile": "Buhar Mobil",
|
||||||
|
"steamChat": "Steam Sohbet",
|
||||||
|
"install": "Yükle",
|
||||||
|
"markInstalled": "Yüklendi olarak İşaretle",
|
||||||
|
"update": "Güncelle",
|
||||||
|
"markUpdated": "Güncellendi olarak İşaretle",
|
||||||
|
"additionalOptions": "Ek Seçenekler",
|
||||||
|
"disableVersionDetection": "Sürüm Algılama Devre Dışı",
|
||||||
|
"noVersionDetectionExplanation": "Bu seçenek, sürüm algılamanın doğru çalışmadığı uygulamalar için kullanılmalıdır.",
|
||||||
|
"downloadingX": "{} İndiriliyor",
|
||||||
|
"downloadNotifDescription": "Bir uygulamanın indirme sürecinde ilerlemeyi bildiren bir bildirim",
|
||||||
|
"noAPKFound": "APK bulunamadı",
|
||||||
|
"noVersionDetection": "Sürüm Algılanamıyor",
|
||||||
|
"categorize": "Kategorize Et",
|
||||||
|
"categories": "Kategoriler",
|
||||||
|
"category": "Kategori",
|
||||||
|
"noCategory": "Kategori Yok",
|
||||||
|
"noCategories": "Kategoriler Yok",
|
||||||
|
"deleteCategoriesQuestion": "Kategorileri Silmek İstiyor musunuz?",
|
||||||
|
"categoryDeleteWarning": "Silinen kategorilerdeki tüm uygulamalar kategorisiz olarak ayarlanacaktır.",
|
||||||
|
"addCategory": "Kategori Ekle",
|
||||||
|
"label": "Etiket",
|
||||||
|
"language": "Dil",
|
||||||
|
"copiedToClipboard": "Panoya Kopyalandı",
|
||||||
|
"storagePermissionDenied": "Depolama izni reddedildi",
|
||||||
|
"selectedCategorizeWarning": "Bu, seçilen uygulamalar için mevcut kategori ayarlarını değiştirecektir.",
|
||||||
|
"filterAPKsByRegEx": "APK'leri Düzenli İfade ile Filtrele",
|
||||||
|
"removeFromObtainium": "Obtainium'dan Kaldır",
|
||||||
|
"uninstallFromDevice": "Cihazdan Kaldır",
|
||||||
|
"onlyWorksWithNonVersionDetectApps": "Yalnızca Sürüm Algılaması Devre Dışı Uygulamalar İçin Çalışır.",
|
||||||
|
"releaseDateAsVersion": "Sürüm Olarak Yayın Tarihi Kullan",
|
||||||
|
"releaseDateAsVersionExplanation": "Bu seçenek, sürüm algılamanın doğru çalışmadığı ancak bir sürüm tarihinin mevcut olduğu uygulamalar için kullanılmalıdır.",
|
||||||
|
"changes": "Değişiklikler",
|
||||||
|
"releaseDate": "Yayın Tarihi",
|
||||||
|
"importFromURLsInFile": "Dosyadaki URL'lerden İçe Aktar",
|
||||||
|
"versionDetectionExplanation": "Sürüm dizesini işletim sistemi tarafından algılanan sürümle uzlaştır",
|
||||||
|
"versionDetection": "Sürüm Tespiti",
|
||||||
|
"standardVersionDetection": "Standart sürüm tespiti",
|
||||||
|
"groupByCategory": "Kategoriye Göre Grupla",
|
||||||
|
"autoApkFilterByArch": "Mümkünse APK'leri CPU mimarisi ile filtreleme girişimi",
|
||||||
|
"overrideSource": "Kaynağı Geçersiz Kıl",
|
||||||
|
"dontShowAgain": "Bunu tekrar gösterme",
|
||||||
|
"dontShowTrackOnlyWarnings": "'Yalnızca Takip Edilen' uyarılarını gösterme",
|
||||||
|
"dontShowAPKOriginWarnings": "APK kaynağı uyarılarını gösterme",
|
||||||
|
"moveNonInstalledAppsToBottom": "Yüklenmemiş Uygulamaları Uygulamalar Görünümünün Altına Taşı",
|
||||||
|
"gitlabPATLabel": "GitLab Kişisel Erişim Belirteci\n(Arama ve Daha İyi APK Keşfi İçin)",
|
||||||
|
"about": "Hakkında",
|
||||||
|
"requiresCredentialsInSettings": "{}: Bu, ek kimlik bilgilerine ihtiyaç duyar (Ayarlar'da)",
|
||||||
|
"checkOnStart": "Başlangıçta güncellemeleri kontrol et",
|
||||||
|
"tryInferAppIdFromCode": "Uygulama kimliğini kaynak kodundan çıkarma girişimi",
|
||||||
|
"removeOnExternalUninstall": "Harici kaldırmada otomatik olarak kaldırılan uygulamalar",
|
||||||
|
"pickHighestVersionCode": "Otomatik olarak en yüksek sürüm kodlu APK'yi seç",
|
||||||
|
"checkUpdateOnDetailPage": "Uygulama detay sayfasını açarken güncellemeleri kontrol et",
|
||||||
|
"disablePageTransitions": "Sayfa geçiş animasyonlarını devre dışı bırak",
|
||||||
|
"reversePageTransitions": "Sayfa geçiş animasyonlarını tersine çevir",
|
||||||
|
"minStarCount": "Minimum Yıldız Sayısı",
|
||||||
|
"addInfoBelow": "Bu bilgiyi aşağıya ekle.",
|
||||||
|
"addInfoInSettings": "Bu bilgiyi Ayarlar'da ekleyin.",
|
||||||
|
"githubSourceNote": "GitHub hız sınırlaması bir API anahtarı kullanılarak atlanabilir.",
|
||||||
|
"gitlabSourceNote": "GitLab APK çıkarma işlemi bir API anahtarı olmadan çalışmayabilir.",
|
||||||
|
"sortByLastLinkSegment": "Bağlantının yalnızca son bölümüne göre sırala",
|
||||||
|
"filterReleaseNotesByRegEx": "Sürüm Notlarını Düzenli İfade ile Filtrele",
|
||||||
|
"customLinkFilterRegex": "Özel APK Bağlantı Filtresi Düzenli İfade ile (Varsayılan '.apk$')",
|
||||||
|
"appsPossiblyUpdated": "Uygulama Güncellemeleri Denendi",
|
||||||
|
"appsPossiblyUpdatedNotifDescription": "Kullanıcıya bir veya daha fazla uygulamanın arka planda potansiyel olarak güncellendiğini bildiren bildirim",
|
||||||
|
"xWasPossiblyUpdatedToY": "{} muhtemelen {} sürümüne güncellendi.",
|
||||||
|
"enableBackgroundUpdates": "Arka plan güncellemelerini etkinleştir",
|
||||||
|
"backgroundUpdateReqsExplanation": "Arka plan güncellemeleri tüm uygulamalar için mümkün olmayabilir.",
|
||||||
|
"backgroundUpdateLimitsExplanation": "Arka plan kurulumunun başarısı, Obtainium'un açıldığında ancak belirlenebilir.",
|
||||||
|
"verifyLatestTag": "'latest' etiketini doğrula",
|
||||||
|
"intermediateLinkRegex": "Ziyaret Edilecek 'Orta Düzey' Bağlantıyı Filtrele",
|
||||||
|
"filterByLinkText": "Bağlantıları bağlantı metnine göre filtrele",
|
||||||
|
"intermediateLinkNotFound": "Ara bağlantı bulunamadı",
|
||||||
|
"intermediateLink": "Ara bağlantı",
|
||||||
|
"exemptFromBackgroundUpdates": "Arka plan güncellemelerinden muaf tut (etkinse)",
|
||||||
|
"bgUpdatesOnWiFiOnly": "WiFi olmadığında arka plan güncellemelerini devre dışı bırak",
|
||||||
|
"autoSelectHighestVersionCode": "Otomatik olarak en yüksek sürüm kodunu seç",
|
||||||
|
"versionExtractionRegEx": "Sürüm Çıkarma Düzenli İfade",
|
||||||
|
"matchGroupToUse": "Sürüm Çıkarma Regex için Kullanılacak Eşleşme Grubu",
|
||||||
|
"highlightTouchTargets": "Daha az belirgin dokunma hedeflerini vurgula",
|
||||||
|
"pickExportDir": "Dışa Aktarılacak Klasörü Seç",
|
||||||
|
"autoExportOnChanges": "Değişikliklerde otomatik olarak dışa aktar",
|
||||||
|
"includeSettings": "Ayarları dahil et",
|
||||||
|
"filterVersionsByRegEx": "Sürümleri Düzenli İfade ile Filtrele",
|
||||||
|
"trySelectingSuggestedVersionCode": "Önerilen sürüm kodunu seçmeyi dene",
|
||||||
|
"dontSortReleasesList": "API'den sıralama düzenini koru",
|
||||||
|
"reverseSort": "Ters sıralama",
|
||||||
|
"takeFirstLink": "İlk bağlantıyı al",
|
||||||
|
"skipSort": "Sıralamayı atla",
|
||||||
|
"debugMenu": "Hata Ayıklama Menüsü",
|
||||||
|
"bgTaskStarted": "Arka plan görevi başladı - günlükleri kontrol et.",
|
||||||
|
"runBgCheckNow": "Arka Plan Güncelleme Kontrolünü Şimdi Çalıştır",
|
||||||
|
"versionExtractWholePage": "Tüm Sayfaya Sürüm Çıkarma Regex'i Uygula",
|
||||||
|
"installing": "Yükleniyor",
|
||||||
|
"skipUpdateNotifications": "Güncelleme bildirimlerini atla",
|
||||||
|
"updatesAvailableNotifChannel": "Güncellemeler Mevcut",
|
||||||
|
"appsUpdatedNotifChannel": "Güncellenen Uygulamalar",
|
||||||
|
"appsPossiblyUpdatedNotifChannel": "Uygulama Güncellemeleri Denendi",
|
||||||
|
"errorCheckingUpdatesNotifChannel": "Güncellemeler Kontrol Edilirken Hata",
|
||||||
|
"appsRemovedNotifChannel": "Kaldırılan Uygulamalar",
|
||||||
|
"downloadingXNotifChannel": "{} İndiriliyor",
|
||||||
|
"completeAppInstallationNotifChannel": "Uygulama Kurulumu Tamamlandı",
|
||||||
|
"checkingForUpdatesNotifChannel": "Güncellemeler Kontrol Ediliyor",
|
||||||
|
"onlyCheckInstalledOrTrackOnlyApps": "Yalnızca yüklü ve Yalnızca İzleme Uygulamalarını güncelleme",
|
||||||
|
"supportFixedAPKURL": "Sabit APK URL'lerini destekleyin",
|
||||||
|
"selectX": "Seçme {}",
|
||||||
|
"parallelDownloads": "Paralel indirmelere izin ver",
|
||||||
|
"installMethod": "Kurulum yöntemi",
|
||||||
|
"normal": "Normal",
|
||||||
|
"root": "Kök",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
|
"useSystemFont": "Sistem yazı tipini kullan",
|
||||||
|
"systemFontError": "Sistem yazı tipi yüklenirken hata oluştu: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Uygulama versionCode'unu işletim sistemi tarafından algılanan sürüm olarak kullan",
|
||||||
|
"requestHeader": "Başlık talep et",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Yayın tarihi olarak en son öğe yüklemesini kullan",
|
||||||
|
"defaultPseudoVersioningMethod": "Varsayılan Sözde Sürüm Oluşturma Yöntemi",
|
||||||
|
"partialAPKHash": "Kısmi APK Karması",
|
||||||
|
"APKLinkHash": "APK Bağlantı Karması",
|
||||||
|
"directAPKLink": "Doğrudan APK Bağlantısı",
|
||||||
|
"pseudoVersionInUse": "Sözde Versiyon Kullanımda",
|
||||||
|
"installed": "Kurulmuş",
|
||||||
|
"latest": "En sonuncu",
|
||||||
|
"invertRegEx": "Normal ifadeyi ters çevir",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
|
"removeAppQuestion": {
|
||||||
|
"one": "Uygulamayı Kaldır?",
|
||||||
|
"other": "Uygulamaları Kaldır?"
|
||||||
|
},
|
||||||
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
|
"one": "Çok fazla istek (hız sınırlı) - {} dakika sonra tekrar deneyin",
|
||||||
|
"other": "Çok fazla istek (hız sınırlı) - {} dakika sonra tekrar deneyin"
|
||||||
|
},
|
||||||
|
"bgUpdateGotErrorRetryInMinutes": {
|
||||||
|
"one": "Arka plan güncelleme kontrolü bir hatayla karşılaştı, {} dakika sonra tekrar kontrol edilecek",
|
||||||
|
"other": "Arka plan güncelleme kontrolü bir hatayla karşılaştı, {} dakika sonra tekrar kontrol edilecek"
|
||||||
|
},
|
||||||
|
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||||
|
"one": "Arka plan güncelleme kontrolü {} güncelleme buldu - gerektiğinde kullanıcıyı bilgilendirecek",
|
||||||
|
"other": "Arka plan güncelleme kontrolü {} güncelleme buldu - gerektiğinde kullanıcıyı bilgilendirecek"
|
||||||
|
},
|
||||||
|
"apps": {
|
||||||
|
"one": "{} Uygulama",
|
||||||
|
"other": "{} Uygulamalar"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"one": "{} URL",
|
||||||
|
"other": "{} URL'ler"
|
||||||
|
},
|
||||||
|
"minute": {
|
||||||
|
"one": "{} Dakika",
|
||||||
|
"other": "{} Dakika"
|
||||||
|
},
|
||||||
|
"hour": {
|
||||||
|
"one": "{} Saat",
|
||||||
|
"other": "{} Saat"
|
||||||
|
},
|
||||||
|
"day": {
|
||||||
|
"one": "{} Gün",
|
||||||
|
"other": "{} Gün"
|
||||||
|
},
|
||||||
|
"clearedNLogsBeforeXAfterY": {
|
||||||
|
"one": "{n} log temizlendi (önce = {before}, sonra = {after})",
|
||||||
|
"other": "{n} log temizlendi (önce = {before}, sonra = {after})"
|
||||||
|
},
|
||||||
|
"xAndNMoreUpdatesAvailable": {
|
||||||
|
"one": "{} ve 1 diğer uygulama güncellemeye sahip.",
|
||||||
|
"other": "{} ve {} daha fazla uygulama güncellemeye sahip."
|
||||||
|
},
|
||||||
|
"xAndNMoreUpdatesInstalled": {
|
||||||
|
"one": "{} ve 1 diğer uygulama güncellendi.",
|
||||||
|
"other": "{} ve {} daha fazla uygulama güncellendi."
|
||||||
|
},
|
||||||
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
|
"one": "{} ve 1 diğer uygulama muhtemelen güncellendi.",
|
||||||
|
"other": "{} ve {} daha fazla uygulama muhtemelen güncellendi."
|
||||||
|
}
|
||||||
|
}
|
||||||
355
assets/translations/vi.json
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
{
|
||||||
|
"invalidURLForSource": "URL ứng dụng {} không hợp lệ",
|
||||||
|
"noReleaseFound": "Không thể tìm thấy bản phát hành phù hợp",
|
||||||
|
"noVersionFound": "Không thể xác định phiên bản phát hành",
|
||||||
|
"urlMatchesNoSource": "URL không khớp với nguồn đã biết",
|
||||||
|
"cantInstallOlderVersion": "Không thể cài đặt phiên bản cũ hơn của Ứng dụng",
|
||||||
|
"appIdMismatch": "ID gói đã tải xuống không khớp với ID ứng dụng hiện tại",
|
||||||
|
"functionNotImplemented": "Lớp này chưa triển khai chức năng này",
|
||||||
|
"placeholder": "Giữ chỗ",
|
||||||
|
"someErrors": "Đã xảy ra một số lỗi",
|
||||||
|
"unexpectedError": "Lỗi không mong đợi",
|
||||||
|
"ok": "OK",
|
||||||
|
"and": "và",
|
||||||
|
"githubPATLabel": "GitHub Token (Tăng tốc độ, giới hạn)",
|
||||||
|
"includePrereleases": "Bao gồm các bản phát hành trước",
|
||||||
|
"fallbackToOlderReleases": "Dự phòng về bản phát hành cũ hơn",
|
||||||
|
"filterReleaseTitlesByRegEx": "Lọc tiêu đề bản phát hành theo biểu thức chính quy",
|
||||||
|
"invalidRegEx": "Biểu thức chính quy không hợp lệ",
|
||||||
|
"noDescription": "Không có mô tả",
|
||||||
|
"cancel": "Hủy",
|
||||||
|
"continue": "Tiếp tục",
|
||||||
|
"requiredInBrackets": "(Yêu cầu)",
|
||||||
|
"dropdownNoOptsError": "LỖI: TẢI XUỐNG PHẢI CÓ ÍT NHẤT MỘT LỰA CHỌN",
|
||||||
|
"colour": "Màu sắc",
|
||||||
|
"githubStarredRepos": "Kho lưu trữ có gắn dấu sao GitHub",
|
||||||
|
"uname": "Tên người dùng",
|
||||||
|
"wrongArgNum": "Số lượng đối số được cung cấp sai",
|
||||||
|
"xIsTrackOnly": "{} là Chỉ theo dõi",
|
||||||
|
"source": "Nguồn",
|
||||||
|
"app": "Ứng dụng",
|
||||||
|
"appsFromSourceAreTrackOnly": "Các ứng dụng từ nguồn này là 'Chỉ theo dõi'.",
|
||||||
|
"youPickedTrackOnly": "Bạn đã chọn tùy chọn 'Chỉ theo dõi'.",
|
||||||
|
"trackOnlyAppDescription": "Ứng dụng sẽ được theo dõi để cập nhật, nhưng Obtainium sẽ không thể tải xuống hoặc cài đặt nó.",
|
||||||
|
"cancelled": "Đã hủy",
|
||||||
|
"appAlreadyAdded": "Ứng dụng được thêm rồi",
|
||||||
|
"alreadyUpToDateQuestion": "Ứng dụng đã được cập nhật?",
|
||||||
|
"addApp": "Thêm",
|
||||||
|
"appSourceURL": "URL nguồn ứng dụng",
|
||||||
|
"error": "Lỗi",
|
||||||
|
"add": "Thêm",
|
||||||
|
"searchSomeSourcesLabel": "Tìm kiếm (Chỉ một số nguồn)",
|
||||||
|
"search": "Tìm kiếm",
|
||||||
|
"additionalOptsFor": "Tùy chọn bổ sung cho {}",
|
||||||
|
"supportedSources": "Nguồn được hỗ trợ",
|
||||||
|
"trackOnlyInBrackets": "(Chỉ theo dõi)",
|
||||||
|
"searchableInBrackets": "(Có thể tìm kiếm)",
|
||||||
|
"appsString": "Ứng dụng",
|
||||||
|
"noApps": "Không có ứng dụng",
|
||||||
|
"noAppsForFilter": "Không có ứng dụng cho bộ lọc",
|
||||||
|
"byX": "Bởi {}",
|
||||||
|
"percentProgress": "Đang tải {}%",
|
||||||
|
"pleaseWait": "Vui lòng chờ",
|
||||||
|
"updateAvailable": "Có sẵn bản cập nhật",
|
||||||
|
"notInstalled": "Chưa cài đặt",
|
||||||
|
"pseudoVersion": "phiên bản giả",
|
||||||
|
"selectAll": "Chọn tất cả",
|
||||||
|
"deselectX": "Bỏ chọn {}",
|
||||||
|
"xWillBeRemovedButRemainInstalled": "{} sẽ bị xóa khỏi Obtainium nhưng vẫn còn cài đặt trên thiết bị.",
|
||||||
|
"removeSelectedAppsQuestion": "Xóa ứng dụng đã chọn?",
|
||||||
|
"removeSelectedApps": "Xóa ứng dụng đã chọn",
|
||||||
|
"updateX": "Cập nhật {}",
|
||||||
|
"installX": "Cài đặt {}",
|
||||||
|
"markXTrackOnlyAsUpdated": "Đánh dấu {}\n(Chỉ theo dõi)\nnhư là đã cập nhật",
|
||||||
|
"changeX": "Thay đổi {}",
|
||||||
|
"installUpdateApps": "Cài đặt/Cập nhật ứng dụng",
|
||||||
|
"installUpdateSelectedApps": "Cài đặt/Cập nhật ứng dụng đã chọn",
|
||||||
|
"markXSelectedAppsAsUpdated": "Đánh dấu {} ứng dụng đã chọn là đã cập nhật?",
|
||||||
|
"no": "Không",
|
||||||
|
"yes": "Đúng",
|
||||||
|
"markSelectedAppsUpdated": "Đánh dấu các ứng dụng đã chọn là đã cập nhật",
|
||||||
|
"pinToTop": "Ghim đầu trang",
|
||||||
|
"unpinFromTop": "Bỏ ghim khỏi đầu trang",
|
||||||
|
"resetInstallStatusForSelectedAppsQuestion": "Đặt lại trạng thái cài đặt cho ứng dụng đã chọn?",
|
||||||
|
"installStatusOfXWillBeResetExplanation": "Trạng thái cài đặt của mọi Ứng dụng đã chọn sẽ được đặt lại.\n\nĐiều này có thể hữu ích khi phiên bản Ứng dụng hiển thị trong Obtainium không chính xác do cập nhật không thành công hoặc các sự cố khác.",
|
||||||
|
"customLinkMessage": "Các liên kết này hoạt động trên các thiết bị có cài đặt Gainium",
|
||||||
|
"shareAppConfigLinks": "Chia sẻ cấu hình ứng dụng dưới dạng liên kết HTML",
|
||||||
|
"shareSelectedAppURLs": "Chia sẻ URL ứng dụng đã chọn",
|
||||||
|
"resetInstallStatus": "Đặt lại trạng thái cài đặt",
|
||||||
|
"more": "Nhiều hơn",
|
||||||
|
"removeOutdatedFilter": "Xóa bộ lọc ứng dụng lỗi thời",
|
||||||
|
"showOutdatedOnly": "Chỉ hiển thị các ứng dụng lỗi thời",
|
||||||
|
"filter": "Lọc",
|
||||||
|
"filterActive": "Lọc *",
|
||||||
|
"filterApps": "Lọc ứng dụng",
|
||||||
|
"appName": "Tên ứng dụng",
|
||||||
|
"author": "Tác giả",
|
||||||
|
"upToDateApps": "Ứng dụng cập nhật",
|
||||||
|
"nonInstalledApps": "Ứng dụng chưa được cài đặt",
|
||||||
|
"importExport": "Nhập/Xuất",
|
||||||
|
"settings": "Cài đặt",
|
||||||
|
"exportedTo": "Đã xuất sang {}",
|
||||||
|
"obtainiumExport": "Xuất",
|
||||||
|
"invalidInput": "Đầu vào không hợp lệ",
|
||||||
|
"importedX": "Đã nhập {}",
|
||||||
|
"obtainiumImport": "Nhập",
|
||||||
|
"importFromURLList": "Nhập từ danh sách URL",
|
||||||
|
"searchQuery": "Truy vấn tìm kiếm",
|
||||||
|
"appURLList": "Danh sách URL ứng dụng",
|
||||||
|
"line": "Hàng",
|
||||||
|
"searchX": "Tìm kiếm {}",
|
||||||
|
"noResults": "Không có kết quả nào được tìm thấy",
|
||||||
|
"importX": "Nhập {}",
|
||||||
|
"importedAppsIdDisclaimer": "Ứng dụng đã nhập có thể hiển thị không chính xác là \"Chưa được cài đặt\".\nĐể khắc phục sự cố này, hãy cài đặt lại chúng thông qua Obtainium.\nĐiều này sẽ không ảnh hưởng đến dữ liệu Ứng dụng.\n\nChỉ ảnh hưởng đến URL và phương thức nhập của bên thứ ba.",
|
||||||
|
"importErrors": "Lỗi nhập",
|
||||||
|
"importedXOfYApps": "{} trong số {} Ứng dụng đã được nhập.",
|
||||||
|
"followingURLsHadErrors": "Các URL sau có lỗi:",
|
||||||
|
"selectURL": "Chọn URL",
|
||||||
|
"selectURLs": "Chọn URL",
|
||||||
|
"pick": "Chọn",
|
||||||
|
"theme": "Chủ đề",
|
||||||
|
"dark": "Tối",
|
||||||
|
"light": "Sáng",
|
||||||
|
"followSystem": "Theo hệ thống",
|
||||||
|
"useBlackTheme": "Nền đen",
|
||||||
|
"appSortBy": "Sắp xếp ứng dụng",
|
||||||
|
"authorName": "Tác giả",
|
||||||
|
"nameAuthor": "Tên",
|
||||||
|
"asAdded": "Như đã thêm",
|
||||||
|
"appSortOrder": "Thứ tự sắp xếp",
|
||||||
|
"ascending": "Tăng dần",
|
||||||
|
"descending": "Giảm dần",
|
||||||
|
"bgUpdateCheckInterval": "Thời gian tự động kiểm tra cập nhật",
|
||||||
|
"neverManualOnly": "Không bao giờ",
|
||||||
|
"appearance": "Hiển thị",
|
||||||
|
"showWebInAppView": "Hiển thị trang web Nguồn trong chế độ xem chi tiết Ứng dụng",
|
||||||
|
"pinUpdates": "Chuyển ứng dụng có phiên bản mới lên đầu danh sách",
|
||||||
|
"updates": "Cập nhật",
|
||||||
|
"sourceSpecific": "Cài đặt Nguồn",
|
||||||
|
"appSource": "Nguồn ứng dụng",
|
||||||
|
"noLogs": "Không có nhật ký",
|
||||||
|
"appLogs": "Nhật ký ứng dụng",
|
||||||
|
"close": "Đóng",
|
||||||
|
"share": "Chia sẻ",
|
||||||
|
"appNotFound": "Không tìm thấy ứng dụng",
|
||||||
|
"obtainiumExportHyphenatedLowercase": "xuất khẩu-obtainium",
|
||||||
|
"pickAnAPK": "Chọn một APK",
|
||||||
|
"appHasMoreThanOnePackage": "{} có nhiều gói:",
|
||||||
|
"deviceSupportsXArch": "Thiết bị của bạn hỗ trợ kiến trúc CPU {}.",
|
||||||
|
"deviceSupportsFollowingArchs": "Thiết bị của bạn hỗ trợ các kiến trúc CPU sau:",
|
||||||
|
"warning": "Cảnh báo",
|
||||||
|
"sourceIsXButPackageFromYPrompt": "Nguồn ứng dụng là '{}' nhưng gói phát hành đến từ '{}'. Tiếp tục?",
|
||||||
|
"updatesAvailable": "Cập nhật có sẵn",
|
||||||
|
"updatesAvailableNotifDescription": "Thông báo cho người dùng rằng có bản cập nhật cho một hoặc nhiều Ứng dụng được theo dõi bởi Obtainium",
|
||||||
|
"noNewUpdates": "Không có bản cập nhật mới.",
|
||||||
|
"xHasAnUpdate": "{} có bản cập nhật.",
|
||||||
|
"appsUpdated": "Ứng dụng đã cập nhật ",
|
||||||
|
"appsUpdatedNotifDescription": "Thông báo cho người dùng rằng các bản cập nhật cho một hoặc nhiều Ứng dụng đã được áp dụng trong nền",
|
||||||
|
"xWasUpdatedToY": "{} đã được cập nhật thành {}.",
|
||||||
|
"errorCheckingUpdates": "Lỗi kiểm tra bản cập nhật",
|
||||||
|
"errorCheckingUpdatesNotifDescription": "Thông báo hiển thị khi kiểm tra cập nhật nền không thành công",
|
||||||
|
"appsRemoved": "Ứng dụng đã loại bỏ",
|
||||||
|
"appsRemovedNotifDescription": "Thông báo cho người dùng rằng một hoặc nhiều Ứng dụng đã bị loại bỏ do lỗi khi tải chúng",
|
||||||
|
"xWasRemovedDueToErrorY": "{} đã bị loại bỏ do lỗi này: {}",
|
||||||
|
"completeAppInstallation": "Hoàn tất cài đặt ứng dụng",
|
||||||
|
"obtainiumMustBeOpenToInstallApps": "Obtainium phải được mở để cài đặt Ứng dụng",
|
||||||
|
"completeAppInstallationNotifDescription": "Yêu cầu người dùng quay lại Obtainium để hoàn tất cài đặt Ứng dụng",
|
||||||
|
"checkingForUpdates": "Đang kiểm tra cập nhật",
|
||||||
|
"checkingForUpdatesNotifDescription": "Thông báo tạm thời xuất hiện khi kiểm tra bản cập nhật",
|
||||||
|
"pleaseAllowInstallPerm": "Vui lòng cho phép Obtainium cài đặt Ứng dụng",
|
||||||
|
"trackOnly": "Chỉ theo dõi",
|
||||||
|
"errorWithHttpStatusCode": "Lỗi {}",
|
||||||
|
"versionCorrectionDisabled": "Tính năng sửa phiên bản bị vô hiệu hóa (plugin dường như không hoạt động)",
|
||||||
|
"unknown": "Không xác định",
|
||||||
|
"none": "Không",
|
||||||
|
"never": "Không bao giờ",
|
||||||
|
"latestVersionX": "Phiên bản mới nhất: {}",
|
||||||
|
"installedVersionX": "Phiên bản đã cài đặt: {}",
|
||||||
|
"lastUpdateCheckX": "Kiểm tra cập nhật lần cuối: {}",
|
||||||
|
"remove": "Loại bỏ",
|
||||||
|
"yesMarkUpdated": "Có, Đánh dấu là đã cập nhật",
|
||||||
|
"fdroid": "Chính thức của F-Droid",
|
||||||
|
"appIdOrName": "ID hoặc tên ứng dụng",
|
||||||
|
"appId": "ID ứng dụng",
|
||||||
|
"appWithIdOrNameNotFound": "Không tìm thấy ứng dụng nào có ID hoặc tên đó",
|
||||||
|
"reposHaveMultipleApps": "Kho có thể chứa nhiều Ứng dụng",
|
||||||
|
"fdroidThirdPartyRepo": "Kho lưu trữ bên thứ ba F-Droid",
|
||||||
|
"steamMobile": "Điện thoại di động hơi nước",
|
||||||
|
"steamChat": "Trò chuyện hơi nước",
|
||||||
|
"install": "Cài đặt",
|
||||||
|
"markInstalled": "Đánh dấu là đã cài đặt",
|
||||||
|
"update": "Cập nhật",
|
||||||
|
"markUpdated": "Đánh dấu đã cập nhật",
|
||||||
|
"additionalOptions": "Tùy chọn bổ sung",
|
||||||
|
"disableVersionDetection": "Tắt tính năng phát hiện phiên bản",
|
||||||
|
"noVersionDetectionExplanation": "Chỉ nên sử dụng tùy chọn này cho Ứng dụng mà tính năng phát hiện phiên bản không hoạt động chính xác.",
|
||||||
|
"downloadingX": "Đang tải xuống {}",
|
||||||
|
"downloadNotifDescription": "Thông báo cho người dùng về tiến trình tải xuống Ứng dụng",
|
||||||
|
"noAPKFound": "Không tìm thấy APK",
|
||||||
|
"noVersionDetection": "Không phát hiện phiên bản",
|
||||||
|
"categorize": "Phân loại",
|
||||||
|
"categories": "Thể loại",
|
||||||
|
"category": "Thể loại",
|
||||||
|
"noCategory": "Không thể loại",
|
||||||
|
"noCategories": "Không thể loại",
|
||||||
|
"deleteCategoriesQuestion": "Xóa thể loại?",
|
||||||
|
"categoryDeleteWarning": "Tất cả ứng dụng trong thể loại đã xóa sẽ được đặt thành chưa được phân loại.",
|
||||||
|
"addCategory": "Thêm thể loại",
|
||||||
|
"label": "Nhãn",
|
||||||
|
"language": "Ngôn ngữ",
|
||||||
|
"copiedToClipboard": "Sao chép vào clipboard",
|
||||||
|
"storagePermissionDenied": "Quyền lưu trữ bị từ chối",
|
||||||
|
"selectedCategorizeWarning": "Điều này sẽ thay thế mọi cài đặt thể loại hiện có cho Ứng dụng đã chọn.",
|
||||||
|
"filterAPKsByRegEx": "Lọc APK theo biểu thức chính quy",
|
||||||
|
"removeFromObtainium": "Loại khỏi Obtainium",
|
||||||
|
"uninstallFromDevice": "Gỡ cài đặt khỏi thiết bị",
|
||||||
|
"onlyWorksWithNonVersionDetectApps": "Chỉ hoạt động với Ứng dụng đã tắt tính năng phát hiện phiên bản.",
|
||||||
|
"releaseDateAsVersion": "Sử dụng ngày phát hành làm phiên bản",
|
||||||
|
"releaseDateAsVersionExplanation": "Chỉ nên sử dụng tùy chọn này cho Ứng dụng trong đó tính năng phát hiện phiên bản không hoạt động chính xác nhưng đã có ngày phát hành.",
|
||||||
|
"changes": "Thay đổi",
|
||||||
|
"releaseDate": "Ngày phát hành",
|
||||||
|
"importFromURLsInFile": "Nhập từ URL trong Tệp (như OPML)",
|
||||||
|
"versionDetectionExplanation": "Đối chiếu chuỗi phiên bản với phiên bản được phát hiện từ hệ điều hành",
|
||||||
|
"versionDetection": "Phát hiện phiên bản",
|
||||||
|
"standardVersionDetection": "Phát hiện phiên bản tiêu chuẩn",
|
||||||
|
"groupByCategory": "Nhóm theo thể loại",
|
||||||
|
"autoApkFilterByArch": "Cố gắng lọc APK theo kiến trúc CPU nếu có thể",
|
||||||
|
"overrideSource": "Ghi đè nguồn",
|
||||||
|
"dontShowAgain": "Đừng hiển thị thông tin này nữa",
|
||||||
|
"dontShowTrackOnlyWarnings": "Không hiển thị cảnh báo 'Chỉ theo dõi'",
|
||||||
|
"dontShowAPKOriginWarnings": "Không hiển thị cảnh báo nguồn gốc APK",
|
||||||
|
"moveNonInstalledAppsToBottom": "Chuyển Ứng dụng chưa được cài đặt xuống cuối danh sách",
|
||||||
|
"gitlabPATLabel": "GitLab Token\n(Cho phép tìm kiếm và lọc APK tốt hơn)",
|
||||||
|
"about": "Giới thiệu",
|
||||||
|
"requiresCredentialsInSettings": "{}: Điều này cần thông tin xác thực bổ sung (trong Cài đặt)",
|
||||||
|
"checkOnStart": "Kiểm tra các bản cập nhật khi khởi động",
|
||||||
|
"tryInferAppIdFromCode": "Thử suy ra ID ứng dụng từ mã nguồn",
|
||||||
|
"removeOnExternalUninstall": "Tự động xóa ứng dụng đã gỡ cài đặt bên ngoài",
|
||||||
|
"pickHighestVersionCode": "Tự động chọn APK mã phiên bản cao nhất",
|
||||||
|
"checkUpdateOnDetailPage": "Kiểm tra các bản cập nhật khi mở trang chi tiết Ứng dụng",
|
||||||
|
"disablePageTransitions": "Tắt hiệu ứng chuyển trang",
|
||||||
|
"reversePageTransitions": "Hoạt ảnh chuyển đổi trang đảo ngược",
|
||||||
|
"minStarCount": "Số lượng sao tối thiểu",
|
||||||
|
"addInfoBelow": "Thêm thông tin này vào bên dưới.",
|
||||||
|
"addInfoInSettings": "Thêm thông tin này vào Cài đặt.",
|
||||||
|
"githubSourceNote": "Có thể tránh được việc giới hạn tốc độ GitHub bằng cách sử dụng khóa API.",
|
||||||
|
"gitlabSourceNote": "Trích xuất APK GitLab có thể không hoạt động nếu không có khóa API.",
|
||||||
|
"sortByLastLinkSegment": "Chỉ sắp xếp theo đoạn cuối của liên kết",
|
||||||
|
"filterReleaseNotesByRegEx": "Lọc ghi chú phát hành theo biểu thức chính quy",
|
||||||
|
"customLinkFilterRegex": "Bộ lọc liên kết APK tùy chỉnh theo biểu thức chính quy (Mặc định '.apk$')",
|
||||||
|
"appsPossiblyUpdated": "Đã cố gắng cập nhật ứng dụng",
|
||||||
|
"appsPossiblyUpdatedNotifDescription": "Thông báo cho người dùng rằng các bản cập nhật cho một hoặc nhiều Ứng dụng có khả năng được áp dụng trong nền",
|
||||||
|
"xWasPossiblyUpdatedToY": "{} có thể đã được cập nhật thành {}.",
|
||||||
|
"enableBackgroundUpdates": "Tự động cập nhật trong nền",
|
||||||
|
"backgroundUpdateReqsExplanation": "Có thể không thực hiện được cập nhật trong nền cho tất cả ứng dụng.",
|
||||||
|
"backgroundUpdateLimitsExplanation": "Sự thành công của cài đặt trong nền chỉ có thể được xác định khi mở Obtainium.",
|
||||||
|
"verifyLatestTag": "Xác minh thẻ 'mới nhất'",
|
||||||
|
"intermediateLinkRegex": "Lọc tìm liên kết 'Trung cấp' để truy cập",
|
||||||
|
"filterByLinkText": "Lọc liên kết theo văn bản liên kết",
|
||||||
|
"intermediateLinkNotFound": "Không tìm thấy liên kết trung gian",
|
||||||
|
"intermediateLink": "Liên kết trung gian",
|
||||||
|
"exemptFromBackgroundUpdates": "Miễn cập nhật nền (nếu được bật)",
|
||||||
|
"bgUpdatesOnWiFiOnly": "Tắt cập nhật nền khi không có WiFi",
|
||||||
|
"autoSelectHighestVersionCode": "Tự động chọn APK mã phiên bản cao nhất",
|
||||||
|
"versionExtractionRegEx": "Trích xuất phiên bản RegEx",
|
||||||
|
"matchGroupToUse": "Nhóm đối sánh để sử dụng cho Regex trích xuất phiên bản",
|
||||||
|
"highlightTouchTargets": "Đánh dấu các mục tiêu cảm ứng ít rõ ràng hơn",
|
||||||
|
"pickExportDir": "Chọn thư mục xuất",
|
||||||
|
"autoExportOnChanges": "Tự động xuất",
|
||||||
|
"includeSettings": "Bao gồm cài đặt ứng dụng",
|
||||||
|
"filterVersionsByRegEx": "Lọc phiên bản theo biểu thức chính quy",
|
||||||
|
"trySelectingSuggestedVersionCode": "Thử chọn APK Mã phiên bản được đề xuất",
|
||||||
|
"dontSortReleasesList": "Giữ lại thứ tự phát hành từ API",
|
||||||
|
"reverseSort": "Sắp xếp ngược",
|
||||||
|
"takeFirstLink": "Lấy liên kết đầu tiên",
|
||||||
|
"skipSort": "Bỏ qua việc sắp xếp",
|
||||||
|
"debugMenu": "Danh sách gỡ lỗi",
|
||||||
|
"bgTaskStarted": "Tác vụ nền đã bắt đầu - kiểm tra nhật ký.",
|
||||||
|
"runBgCheckNow": "Chạy kiểm tra cập nhật nền ngay bây giờ",
|
||||||
|
"versionExtractWholePage": "Áp dụng Regex trích xuất phiên bản cho toàn bộ trang",
|
||||||
|
"installing": "Đang cài đặt",
|
||||||
|
"skipUpdateNotifications": "Bỏ qua thông báo cập nhật",
|
||||||
|
"updatesAvailableNotifChannel": "Cập nhật có sẵn",
|
||||||
|
"appsUpdatedNotifChannel": "Đã cập nhật ứng dụng",
|
||||||
|
"appsPossiblyUpdatedNotifChannel": "Đã cố gắng cập nhật ứng dụng",
|
||||||
|
"errorCheckingUpdatesNotifChannel": "Lỗi kiểm tra bản cập nhật",
|
||||||
|
"appsRemovedNotifChannel": "Ứng dụng đã bị loại bỏ",
|
||||||
|
"downloadingXNotifChannel": "Đang tải xuống {}",
|
||||||
|
"completeAppInstallationNotifChannel": "Hoàn tất cài đặt ứng dụng",
|
||||||
|
"checkingForUpdatesNotifChannel": "Đang kiểm tra cập nhật",
|
||||||
|
"onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra cập nhật các ứng dụng đã cài đặt và Chỉ theo dõi",
|
||||||
|
"supportFixedAPKURL": "Hỗ trợ URL APK cố định",
|
||||||
|
"selectX": "Lựa chọn {}",
|
||||||
|
"parallelDownloads": "Cho phép tải đa luồng",
|
||||||
|
"installMethod": "Phương thức cài đặt",
|
||||||
|
"normal": "Mặc định",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku chưa khởi động",
|
||||||
|
"useSystemFont": "Sử dụng phông chữ hệ thống",
|
||||||
|
"systemFontError": "Lỗi tải phông chữ hệ thống: {}",
|
||||||
|
"useVersionCodeAsOSVersion": "Sử dụng Mã phiên bản ứng dụng làm phiên bản do hệ điều hành phát hiện",
|
||||||
|
"requestHeader": "Tiêu đề yêu cầu",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "Sử dụng nội dung tải lên mới nhất làm ngày phát hành",
|
||||||
|
"defaultPseudoVersioningMethod": "Phương pháp tạo phiên bản giả mặc định",
|
||||||
|
"partialAPKHash": "Băm APK một phần",
|
||||||
|
"APKLinkHash": "Băm liên kết APK",
|
||||||
|
"directAPKLink": "Liên kết APK trực tiếp",
|
||||||
|
"pseudoVersionInUse": "Phiên bản giả đang được sử dụng",
|
||||||
|
"installed": "Đã cài đặt",
|
||||||
|
"latest": "Mới nhất",
|
||||||
|
"invertRegEx": "Đảo ngược biểu thức chính quy",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
|
"removeAppQuestion": {
|
||||||
|
"one": "Gỡ ứng dụng?",
|
||||||
|
"other": "Gỡ ứng dụng?"
|
||||||
|
},
|
||||||
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
|
"one": "Quá nhiều yêu cầu (tốc độ giới hạn) - hãy thử lại sau {} phút",
|
||||||
|
"other": "Quá nhiều yêu cầu (tốc độ giới hạn) - hãy thử lại sau {} phút"
|
||||||
|
},
|
||||||
|
"bgUpdateGotErrorRetryInMinutes": {
|
||||||
|
"one": "Việc kiểm tra bản cập nhật BG gặp phải {}, sẽ lên lịch kiểm tra lại sau {} phút",
|
||||||
|
"other": "Việc kiểm tra bản cập nhật BG gặp phải {}, sẽ lên lịch kiểm tra lại sau {} phút"
|
||||||
|
},
|
||||||
|
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||||
|
"one": "Đang kiểm tra bản cập nhật BG tìm thấy {} bản cập nhật - sẽ thông báo cho người dùng nếu cần",
|
||||||
|
"other": "Đang kiểm tra bản cập nhật BG tìm thấy {} bản cập nhật - sẽ thông báo cho người dùng nếu cần"
|
||||||
|
},
|
||||||
|
"apps": {
|
||||||
|
"one": "{} Ứng dụng",
|
||||||
|
"other": "{} Ứng dụng"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"one": "{} URL",
|
||||||
|
"other": "{} URL"
|
||||||
|
},
|
||||||
|
"minute": {
|
||||||
|
"one": "{} Phút",
|
||||||
|
"other": "{} Phút"
|
||||||
|
},
|
||||||
|
"hour": {
|
||||||
|
"one": "{} Giờ",
|
||||||
|
"other": "{} Giờ"
|
||||||
|
},
|
||||||
|
"day": {
|
||||||
|
"one": "{} Ngày",
|
||||||
|
"other": "{} ngày"
|
||||||
|
},
|
||||||
|
"clearedNLogsBeforeXAfterY": {
|
||||||
|
"one": "Đã xóa {n} nhật ký (trước = {trước}, sau = {sau})",
|
||||||
|
"other": "Đã xóa {n} nhật ký (trước = {trước}, sau = {sau})"
|
||||||
|
},
|
||||||
|
"xAndNMoreUpdatesAvailable": {
|
||||||
|
"one": "{} và 1 ứng dụng khác có bản cập nhật.",
|
||||||
|
"other": "{} và {} ứng dụng khác có bản cập nhật."
|
||||||
|
},
|
||||||
|
"xAndNMoreUpdatesInstalled": {
|
||||||
|
"one": "{} và 1 ứng dụng khác đã được cập nhật.",
|
||||||
|
"other": "{} và {} ứng dụng khác đã được cập nhật."
|
||||||
|
},
|
||||||
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
|
"one": "{} và 1 ứng dụng khác có thể đã được cập nhật.",
|
||||||
|
"other": "{} và {} ứng dụng khác có thể đã được cập nhật."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"githubPATLabel": "GitHub 个人访问令牌(提升 API 请求限额)",
|
"githubPATLabel": "GitHub 个人访问令牌(提升 API 请求限额)",
|
||||||
"includePrereleases": "包含预发行版",
|
"includePrereleases": "包含预发行版",
|
||||||
"fallbackToOlderReleases": "将旧发行版作为备选",
|
"fallbackToOlderReleases": "将旧发行版作为备选",
|
||||||
"filterReleaseTitlesByRegEx": "使用正则表达式筛选发行标题",
|
"filterReleaseTitlesByRegEx": "筛选发行标题(正则表达式)",
|
||||||
"invalidRegEx": "无效的正则表达式",
|
"invalidRegEx": "无效的正则表达式",
|
||||||
"noDescription": "无描述",
|
"noDescription": "无描述",
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
@@ -22,11 +22,11 @@
|
|||||||
"requiredInBrackets": "(必填)",
|
"requiredInBrackets": "(必填)",
|
||||||
"dropdownNoOptsError": "错误:下拉菜单必须包含至少一个选项",
|
"dropdownNoOptsError": "错误:下拉菜单必须包含至少一个选项",
|
||||||
"colour": "配色",
|
"colour": "配色",
|
||||||
"githubStarredRepos": "GitHub 已星标仓库",
|
"githubStarredRepos": "已星标的 GitHub 仓库",
|
||||||
"uname": "用户名",
|
"uname": "用户名",
|
||||||
"wrongArgNum": "参数数量错误",
|
"wrongArgNum": "参数数量错误",
|
||||||
"xIsTrackOnly": "{}为“仅追踪”模式",
|
"xIsTrackOnly": "“{}”为“仅追踪”模式",
|
||||||
"source": "源代码",
|
"source": "来源",
|
||||||
"app": "应用",
|
"app": "应用",
|
||||||
"appsFromSourceAreTrackOnly": "此来源的应用为“仅追踪”模式。",
|
"appsFromSourceAreTrackOnly": "此来源的应用为“仅追踪”模式。",
|
||||||
"youPickedTrackOnly": "您选择了“仅追踪”。",
|
"youPickedTrackOnly": "您选择了“仅追踪”。",
|
||||||
@@ -51,17 +51,16 @@
|
|||||||
"percentProgress": "进度:{}%",
|
"percentProgress": "进度:{}%",
|
||||||
"pleaseWait": "请稍候",
|
"pleaseWait": "请稍候",
|
||||||
"updateAvailable": "更新可用",
|
"updateAvailable": "更新可用",
|
||||||
"estimateInBracketsShort": "(推测)",
|
|
||||||
"notInstalled": "未安装",
|
"notInstalled": "未安装",
|
||||||
"estimateInBrackets": "(推测)",
|
"pseudoVersion": "伪版本",
|
||||||
"selectAll": "全选",
|
"selectAll": "全选",
|
||||||
"deselectN": "取消选择 {}",
|
"deselectX": "取消选择 {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} 将从 Obtainium 中删除,但仍安装在您的设备中。",
|
"xWillBeRemovedButRemainInstalled": "“{}”将从 Obtainium 中删除,但仍安装在您的设备中。",
|
||||||
"removeSelectedAppsQuestion": "是否删除选中的应用?",
|
"removeSelectedAppsQuestion": "是否删除选中的应用?",
|
||||||
"removeSelectedApps": "删除选中的应用",
|
"removeSelectedApps": "删除选中的应用",
|
||||||
"updateX": "更新 {}",
|
"updateX": "更新 {}",
|
||||||
"installX": "安装 {}",
|
"installX": "安装 {}",
|
||||||
"markXTrackOnlyAsUpdated": "将 {}\n(仅追踪)\n标记为已更新",
|
"markXTrackOnlyAsUpdated": "将“{}”\n(仅追踪)\n标记为已更新",
|
||||||
"changeX": "更改 {}",
|
"changeX": "更改 {}",
|
||||||
"installUpdateApps": "安装/更新应用",
|
"installUpdateApps": "安装/更新应用",
|
||||||
"installUpdateSelectedApps": "安装/更新选中的应用",
|
"installUpdateSelectedApps": "安装/更新选中的应用",
|
||||||
@@ -73,6 +72,8 @@
|
|||||||
"unpinFromTop": "取消置顶",
|
"unpinFromTop": "取消置顶",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "是否重置选中应用的安装状态?",
|
"resetInstallStatusForSelectedAppsQuestion": "是否重置选中应用的安装状态?",
|
||||||
"installStatusOfXWillBeResetExplanation": "选中应用的安装状态将会被重置。\n\n当更新安装失败或其他问题导致 Obtainium 中的应用版本显示错误时,可以尝试通过此方法解决。",
|
"installStatusOfXWillBeResetExplanation": "选中应用的安装状态将会被重置。\n\n当更新安装失败或其他问题导致 Obtainium 中的应用版本显示错误时,可以尝试通过此方法解决。",
|
||||||
|
"customLinkMessage": "这些链接适用于安装了 Gettingium 的设备",
|
||||||
|
"shareAppConfigLinks": "将应用程序配置共享为 HTML 链接",
|
||||||
"shareSelectedAppURLs": "分享选中应用的 URL",
|
"shareSelectedAppURLs": "分享选中应用的 URL",
|
||||||
"resetInstallStatus": "重置安装状态",
|
"resetInstallStatus": "重置安装状态",
|
||||||
"more": "更多",
|
"more": "更多",
|
||||||
@@ -88,22 +89,21 @@
|
|||||||
"importExport": "导入/导出",
|
"importExport": "导入/导出",
|
||||||
"settings": "设置",
|
"settings": "设置",
|
||||||
"exportedTo": "已导出至 {}",
|
"exportedTo": "已导出至 {}",
|
||||||
"obtainiumExport": "Obtainium 导出",
|
"obtainiumExport": "导出 Obtainium",
|
||||||
"invalidInput": "无效的输入",
|
"invalidInput": "无效的输入",
|
||||||
"importedX": "已导入 {}",
|
"importedX": "已导入 {}",
|
||||||
"obtainiumImport": "Obtainium 导入",
|
"obtainiumImport": "导入 Obtainium",
|
||||||
"importFromURLList": "从 URL 列表导入",
|
"importFromURLList": "从 URL 列表导入",
|
||||||
"searchQuery": "搜索查询",
|
"searchQuery": "搜索查询",
|
||||||
"appURLList": "应用 URL 列表",
|
"appURLList": "应用 URL 列表",
|
||||||
"line": "行",
|
"line": "行",
|
||||||
"searchX": "搜索 {}",
|
"searchX": "搜索{}",
|
||||||
"noResults": "无结果",
|
"noResults": "无结果",
|
||||||
"importX": "导入 {}",
|
"importX": "导入{}",
|
||||||
"importedAppsIdDisclaimer": "导入的应用可能会错误地显示为“未安装”状态。\n请通过 Obtainium 重新安装这些应用来解决此问题。",
|
"importedAppsIdDisclaimer": "导入的应用可能会错误地显示为“未安装”状态。\n请通过 Obtainium 重新安装这些应用来解决此问题。",
|
||||||
"importErrors": "导入错误",
|
"importErrors": "导入错误",
|
||||||
"importedXOfYApps": "已导入 {} 中的 {} 个应用。",
|
"importedXOfYApps": "已导入 {} 中的 {} 个应用。",
|
||||||
"followingURLsHadErrors": "下列 URL 存在错误:",
|
"followingURLsHadErrors": "下列 URL 存在错误:",
|
||||||
"okay": "好的",
|
|
||||||
"selectURL": "选择 URL",
|
"selectURL": "选择 URL",
|
||||||
"selectURLs": "选择 URL",
|
"selectURLs": "选择 URL",
|
||||||
"pick": "选择",
|
"pick": "选择",
|
||||||
@@ -111,8 +111,6 @@
|
|||||||
"dark": "深色",
|
"dark": "深色",
|
||||||
"light": "浅色",
|
"light": "浅色",
|
||||||
"followSystem": "跟随系统",
|
"followSystem": "跟随系统",
|
||||||
"obtainium": "Obtainium",
|
|
||||||
"materialYou": "Material You",
|
|
||||||
"useBlackTheme": "使用纯黑深色主题",
|
"useBlackTheme": "使用纯黑深色主题",
|
||||||
"appSortBy": "排序依据",
|
"appSortBy": "排序依据",
|
||||||
"authorName": "作者 / 应用名称",
|
"authorName": "作者 / 应用名称",
|
||||||
@@ -124,7 +122,7 @@
|
|||||||
"bgUpdateCheckInterval": "后台更新检查间隔",
|
"bgUpdateCheckInterval": "后台更新检查间隔",
|
||||||
"neverManualOnly": "手动",
|
"neverManualOnly": "手动",
|
||||||
"appearance": "外观",
|
"appearance": "外观",
|
||||||
"showWebInAppView": "在应用详情页显示来源网页",
|
"showWebInAppView": "应用详情页显示来源网页",
|
||||||
"pinUpdates": "将待更新应用置顶",
|
"pinUpdates": "将待更新应用置顶",
|
||||||
"updates": "更新",
|
"updates": "更新",
|
||||||
"sourceSpecific": "来源",
|
"sourceSpecific": "来源",
|
||||||
@@ -134,9 +132,9 @@
|
|||||||
"close": "关闭",
|
"close": "关闭",
|
||||||
"share": "分享",
|
"share": "分享",
|
||||||
"appNotFound": "未找到应用",
|
"appNotFound": "未找到应用",
|
||||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
"obtainiumExportHyphenatedLowercase": "获取出口",
|
||||||
"pickAnAPK": "选择一个 APK 文件",
|
"pickAnAPK": "选择一个 APK 文件",
|
||||||
"appHasMoreThanOnePackage": "{} 有多个架构可用:",
|
"appHasMoreThanOnePackage": "“{}”有多个架构可用:",
|
||||||
"deviceSupportsXArch": "您的设备支持 {} 架构。",
|
"deviceSupportsXArch": "您的设备支持 {} 架构。",
|
||||||
"deviceSupportsFollowingArchs": "您的设备支持下列架构:",
|
"deviceSupportsFollowingArchs": "您的设备支持下列架构:",
|
||||||
"warning": "警告",
|
"warning": "警告",
|
||||||
@@ -144,15 +142,15 @@
|
|||||||
"updatesAvailable": "更新可用",
|
"updatesAvailable": "更新可用",
|
||||||
"updatesAvailableNotifDescription": "Obtainium 追踪的应用有更新时发送通知",
|
"updatesAvailableNotifDescription": "Obtainium 追踪的应用有更新时发送通知",
|
||||||
"noNewUpdates": "全部应用已是最新。",
|
"noNewUpdates": "全部应用已是最新。",
|
||||||
"xHasAnUpdate": "{} 可以更新了。",
|
"xHasAnUpdate": "“{}”可以更新了。",
|
||||||
"appsUpdated": "应用已更新",
|
"appsUpdated": "应用已更新",
|
||||||
"appsUpdatedNotifDescription": "当应用在后台安装更新时发送通知",
|
"appsUpdatedNotifDescription": "当应用在后台安装更新时发送通知",
|
||||||
"xWasUpdatedToY": "{} 已更新至 {}。",
|
"xWasUpdatedToY": "“{}”已更新至 {}。",
|
||||||
"errorCheckingUpdates": "检查更新出错",
|
"errorCheckingUpdates": "检查更新出错",
|
||||||
"errorCheckingUpdatesNotifDescription": "当后台检查更新失败时显示的通知",
|
"errorCheckingUpdatesNotifDescription": "当后台检查更新失败时显示的通知",
|
||||||
"appsRemoved": "应用已删除",
|
"appsRemoved": "应用已删除",
|
||||||
"appsRemovedNotifDescription": "当应用因加载出错而被删除时发送通知",
|
"appsRemovedNotifDescription": "当应用因加载出错而被删除时发送通知",
|
||||||
"xWasRemovedDueToErrorY": "{} 由于以下错误被删除:{}",
|
"xWasRemovedDueToErrorY": "“{}”由于以下错误被删除:{}",
|
||||||
"completeAppInstallation": "完成应用安装",
|
"completeAppInstallation": "完成应用安装",
|
||||||
"obtainiumMustBeOpenToInstallApps": "必须启动 Obtainium 才能安装应用",
|
"obtainiumMustBeOpenToInstallApps": "必须启动 Obtainium 才能安装应用",
|
||||||
"completeAppInstallationNotifDescription": "提示返回 Obtainium 以完成应用的安装",
|
"completeAppInstallationNotifDescription": "提示返回 Obtainium 以完成应用的安装",
|
||||||
@@ -160,7 +158,7 @@
|
|||||||
"checkingForUpdatesNotifDescription": "检查更新时短暂显示的通知",
|
"checkingForUpdatesNotifDescription": "检查更新时短暂显示的通知",
|
||||||
"pleaseAllowInstallPerm": "请授予 Obtainium 安装应用的权限",
|
"pleaseAllowInstallPerm": "请授予 Obtainium 安装应用的权限",
|
||||||
"trackOnly": "仅追踪",
|
"trackOnly": "仅追踪",
|
||||||
"errorWithHttpStatusCode": "错误 {}",
|
"errorWithHttpStatusCode": "{} 错误",
|
||||||
"versionCorrectionDisabled": "禁用版本号更正(插件似乎未起作用)",
|
"versionCorrectionDisabled": "禁用版本号更正(插件似乎未起作用)",
|
||||||
"unknown": "未知",
|
"unknown": "未知",
|
||||||
"none": "无",
|
"none": "无",
|
||||||
@@ -176,9 +174,8 @@
|
|||||||
"appWithIdOrNameNotFound": "未找到符合此 ID 或名称的应用",
|
"appWithIdOrNameNotFound": "未找到符合此 ID 或名称的应用",
|
||||||
"reposHaveMultipleApps": "存储库中可能包含多个应用",
|
"reposHaveMultipleApps": "存储库中可能包含多个应用",
|
||||||
"fdroidThirdPartyRepo": "F-Droid 第三方存储库",
|
"fdroidThirdPartyRepo": "F-Droid 第三方存储库",
|
||||||
"steam": "Steam",
|
"steamMobile": "蒸汽手机",
|
||||||
"steamMobile": "Steam Mobile",
|
"steamChat": "蒸汽聊天",
|
||||||
"steamChat": "Steam Chat",
|
|
||||||
"install": "安装",
|
"install": "安装",
|
||||||
"markInstalled": "标记为已安装",
|
"markInstalled": "标记为已安装",
|
||||||
"update": "更新",
|
"update": "更新",
|
||||||
@@ -186,11 +183,11 @@
|
|||||||
"additionalOptions": "附加选项",
|
"additionalOptions": "附加选项",
|
||||||
"disableVersionDetection": "禁用版本检测",
|
"disableVersionDetection": "禁用版本检测",
|
||||||
"noVersionDetectionExplanation": "此选项应该仅用于无法进行版本检测的应用。",
|
"noVersionDetectionExplanation": "此选项应该仅用于无法进行版本检测的应用。",
|
||||||
"downloadingX": "正在下载{}",
|
"downloadingX": "正在下载“{}”",
|
||||||
"downloadNotifDescription": "提示应用的下载进度",
|
"downloadNotifDescription": "提示应用的下载进度",
|
||||||
"noAPKFound": "未找到 APK 文件",
|
"noAPKFound": "未找到 APK 文件",
|
||||||
"noVersionDetection": "禁用版本检测",
|
"noVersionDetection": "禁用版本检测",
|
||||||
"categorize": "分类",
|
"categorize": "归类",
|
||||||
"categories": "类别",
|
"categories": "类别",
|
||||||
"category": "类别",
|
"category": "类别",
|
||||||
"noCategory": "无类别",
|
"noCategory": "无类别",
|
||||||
@@ -203,7 +200,7 @@
|
|||||||
"copiedToClipboard": "已复制至剪贴板",
|
"copiedToClipboard": "已复制至剪贴板",
|
||||||
"storagePermissionDenied": "已拒绝授予存储权限",
|
"storagePermissionDenied": "已拒绝授予存储权限",
|
||||||
"selectedCategorizeWarning": "这将覆盖选中应用当前的类别设置。",
|
"selectedCategorizeWarning": "这将覆盖选中应用当前的类别设置。",
|
||||||
"filterAPKsByRegEx": "使用正则表达式筛选 APK 文件",
|
"filterAPKsByRegEx": "筛选 APK 文件(正则表达式)",
|
||||||
"removeFromObtainium": "从 Obtainium 中删除",
|
"removeFromObtainium": "从 Obtainium 中删除",
|
||||||
"uninstallFromDevice": "从设备中卸载",
|
"uninstallFromDevice": "从设备中卸载",
|
||||||
"onlyWorksWithNonVersionDetectApps": "仅适用于禁用版本检测的应用。",
|
"onlyWorksWithNonVersionDetectApps": "仅适用于禁用版本检测的应用。",
|
||||||
@@ -212,23 +209,24 @@
|
|||||||
"changes": "更新日志",
|
"changes": "更新日志",
|
||||||
"releaseDate": "发行日期",
|
"releaseDate": "发行日期",
|
||||||
"importFromURLsInFile": "从文件中的 URL 导入(如 OPML)",
|
"importFromURLsInFile": "从文件中的 URL 导入(如 OPML)",
|
||||||
|
"versionDetectionExplanation": "将版本字符串与操作系统检测到的版本进行协调",
|
||||||
"versionDetection": "版本检测",
|
"versionDetection": "版本检测",
|
||||||
"standardVersionDetection": "常规版本检测",
|
"standardVersionDetection": "常规版本检测",
|
||||||
"groupByCategory": "按类别分组显示",
|
"groupByCategory": "按类别分组显示",
|
||||||
"autoApkFilterByArch": "如果可能,尝试按设备支持的 CPU 架构筛选 APK 文件",
|
"autoApkFilterByArch": "如果可能,尝试按设备支持的 CPU 架构筛选 APK 文件",
|
||||||
"overrideSource": "覆盖来源",
|
"overrideSource": "覆盖来源",
|
||||||
"dontShowAgain": "不再显示",
|
"dontShowAgain": "不再显示",
|
||||||
"dontShowTrackOnlyWarnings": "不显示“仅追踪”模式警告",
|
"dontShowTrackOnlyWarnings": "忽略“仅追踪”模式警告",
|
||||||
"dontShowAPKOriginWarnings": "不显示 APK 文件来源警告",
|
"dontShowAPKOriginWarnings": "忽略 APK 文件来源警告",
|
||||||
"moveNonInstalledAppsToBottom": "将未安装应用置底",
|
"moveNonInstalledAppsToBottom": "将未安装应用置底",
|
||||||
"gitlabPATLabel": "GitLab 个人访问令牌\n(启用搜索功能并增强 APK 发现)",
|
"gitlabPATLabel": "GitLab 个人访问令牌(启用搜索功能并增强 APK 发现)",
|
||||||
"about": "相关文档",
|
"about": "相关文档",
|
||||||
"requiresCredentialsInSettings": "此功能需要额外的凭据(在“设置”中添加)",
|
"requiresCredentialsInSettings": "{}:此功能需要额外的凭据(在“设置”中添加)",
|
||||||
"checkOnStart": "启动时进行一次检查",
|
"checkOnStart": "启动时进行一次检查",
|
||||||
"tryInferAppIdFromCode": "尝试从源代码推断应用 ID",
|
"tryInferAppIdFromCode": "尝试从源代码推断应用 ID",
|
||||||
"removeOnExternalUninstall": "自动删除已卸载的外部应用",
|
"removeOnExternalUninstall": "自动删除列表中已卸载的应用",
|
||||||
"pickHighestVersionCode": "自动选择版本号最高的 APK 文件",
|
"pickHighestVersionCode": "自动选择版本号最高的 APK 文件",
|
||||||
"checkUpdateOnDetailPage": "打开应用详情页时检查更新",
|
"checkUpdateOnDetailPage": "打开应用详情页时进行检查",
|
||||||
"disablePageTransitions": "禁用页面过渡动画效果",
|
"disablePageTransitions": "禁用页面过渡动画效果",
|
||||||
"reversePageTransitions": "反转页面过渡动画效果",
|
"reversePageTransitions": "反转页面过渡动画效果",
|
||||||
"minStarCount": "最小星标数",
|
"minStarCount": "最小星标数",
|
||||||
@@ -236,33 +234,72 @@
|
|||||||
"addInfoInSettings": "在“设置”中添加此凭据。",
|
"addInfoInSettings": "在“设置”中添加此凭据。",
|
||||||
"githubSourceNote": "使用访问令牌可避免触发 GitHub 的 API 请求限制。",
|
"githubSourceNote": "使用访问令牌可避免触发 GitHub 的 API 请求限制。",
|
||||||
"gitlabSourceNote": "未使用访问令牌时可能无法从 GitLab 获取 APK 文件。",
|
"gitlabSourceNote": "未使用访问令牌时可能无法从 GitLab 获取 APK 文件。",
|
||||||
"sortByFileNamesNotLinks": "使用文件名代替链接进行排序",
|
"sortByLastLinkSegment": "仅根据链接的末尾部分进行筛选",
|
||||||
"filterReleaseNotesByRegEx": "使用正则表达式筛选发行说明",
|
"filterReleaseNotesByRegEx": "筛选发行说明(正则表达式)",
|
||||||
"customLinkFilterRegex": "使用正则表达式自定义链接筛选(默认模式为“.apk$”)",
|
"customLinkFilterRegex": "筛选自定义来源的 APK 文件链接\n(正则表达式,默认匹配模式为“.apk$”)",
|
||||||
"appsPossiblyUpdated": "已尝试更新应用",
|
"appsPossiblyUpdated": "已尝试更新应用",
|
||||||
"appsPossiblyUpdatedNotifDescription": "当应用已尝试在后台更新时发送通知",
|
"appsPossiblyUpdatedNotifDescription": "当应用已尝试在后台更新时发送通知",
|
||||||
"xWasPossiblyUpdatedToY": "已尝试将 {} 更新至 {}。",
|
"xWasPossiblyUpdatedToY": "已尝试将“{}”更新至 {}。",
|
||||||
"enableBackgroundUpdates": "启用后台更新",
|
"enableBackgroundUpdates": "启用后台更新",
|
||||||
"backgroundUpdateReqsExplanation": "后台更新未必适用于所有的应用。",
|
"backgroundUpdateReqsExplanation": "后台更新未必适用于所有的应用。",
|
||||||
"backgroundUpdateLimitsExplanation": "只有在启动 Obtainium 时才能确认安装是否成功。",
|
"backgroundUpdateLimitsExplanation": "只有在启动 Obtainium 时才能确认安装是否成功。",
|
||||||
"verifyLatestTag": "验证“Latest”标签",
|
"verifyLatestTag": "验证“Latest”标签",
|
||||||
"intermediateLinkRegex": "首先访问“中间”链接的过滤器",
|
"intermediateLinkRegex": "筛选中转链接(正则表达式)",
|
||||||
"intermediateLinkNotFound": "中间链接未找到",
|
"filterByLinkText": "根据链接文本进行筛选",
|
||||||
"exemptFromBackgroundUpdates": "禁用后台更新(如果全局设置启用)",
|
"intermediateLinkNotFound": "未找到中转链接",
|
||||||
"bgUpdatesOnWiFiOnly": "不在连接 WiFi 时禁用后台更新",
|
"intermediateLink": "中转链接",
|
||||||
"autoSelectHighestVersionCode": "自动选择最高版本号 APK",
|
"exemptFromBackgroundUpdates": "禁用后台更新(如果已经全局启用)",
|
||||||
"versionExtractionRegEx": "版本提取正则表达式",
|
"bgUpdatesOnWiFiOnly": "未连接 Wi-Fi 时禁用后台更新",
|
||||||
"matchGroupToUse": "匹配要使用的组",
|
"autoSelectHighestVersionCode": "自动选择版本号最高的 APK 文件",
|
||||||
"highlightTouchTargets": "突出显示不明显的触摸目标",
|
"versionExtractionRegEx": "版本号提取规则(正则表达式)",
|
||||||
"pickExportDir": "选择导出目录",
|
"matchGroupToUse": "引用的捕获组",
|
||||||
"autoExportOnChanges": "修改时自动导出",
|
"highlightTouchTargets": "突出展示不明显的触摸区域",
|
||||||
"filterVersionsByRegEx": "使用正则表达式筛选版本",
|
"pickExportDir": "选择导出文件夹",
|
||||||
"trySelectingSuggestedVersionCode": "尝试选择推荐版本 APK",
|
"autoExportOnChanges": "数据变更时自动导出",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"includeSettings": "同时导出应用设置",
|
||||||
"reverseSort": "Reverse sorting",
|
"filterVersionsByRegEx": "筛选版本号(正则表达式)",
|
||||||
"debugMenu": "Debug Menu",
|
"trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"dontSortReleasesList": "保持来自 API 的发行顺序",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"reverseSort": "反转排序",
|
||||||
|
"takeFirstLink": "选取第一个链接",
|
||||||
|
"skipSort": "不进行排序",
|
||||||
|
"debugMenu": "调试选项",
|
||||||
|
"bgTaskStarted": "后台任务已启动 - 详见日志",
|
||||||
|
"runBgCheckNow": "立即进行后台更新检查",
|
||||||
|
"versionExtractWholePage": "将版本号提取规则应用于完整页面",
|
||||||
|
"installing": "正在安装",
|
||||||
|
"skipUpdateNotifications": "忽略更新通知",
|
||||||
|
"updatesAvailableNotifChannel": "更新可用",
|
||||||
|
"appsUpdatedNotifChannel": "应用已更新",
|
||||||
|
"appsPossiblyUpdatedNotifChannel": "已尝试更新应用",
|
||||||
|
"errorCheckingUpdatesNotifChannel": "检查更新出错",
|
||||||
|
"appsRemovedNotifChannel": "应用已删除",
|
||||||
|
"downloadingXNotifChannel": "正在下载“{}”",
|
||||||
|
"completeAppInstallationNotifChannel": "完成应用安装",
|
||||||
|
"checkingForUpdatesNotifChannel": "正在检查更新",
|
||||||
|
"onlyCheckInstalledOrTrackOnlyApps": "只检查已安装和“仅追踪”的应用",
|
||||||
|
"supportFixedAPKURL": "支持固定的 APK 文件链接",
|
||||||
|
"selectX": "选择{}",
|
||||||
|
"parallelDownloads": "启用并行下载",
|
||||||
|
"installMethod": "安装方式",
|
||||||
|
"normal": "常规",
|
||||||
|
"root": "root",
|
||||||
|
"shizukuBinderNotFound": "未发现兼容的 Shizuku 服务",
|
||||||
|
"useSystemFont": "使用系统字体",
|
||||||
|
"systemFontError": "加载系统字体出错:{}",
|
||||||
|
"useVersionCodeAsOSVersion": "使用应用程序版本代码作为操作系统检测到的版本",
|
||||||
|
"requestHeader": "请求标头",
|
||||||
|
"useLatestAssetDateAsReleaseDate": "使用最新资产上传作为发布日期",
|
||||||
|
"defaultPseudoVersioningMethod": "默认伪版本控制方法",
|
||||||
|
"partialAPKHash": "部分 APK 哈希值",
|
||||||
|
"APKLinkHash": "APK 链接哈希",
|
||||||
|
"directAPKLink": "直接 APK 链接",
|
||||||
|
"pseudoVersionInUse": "伪版本正在使用",
|
||||||
|
"installed": "已安装",
|
||||||
|
"latest": "最新的",
|
||||||
|
"invertRegEx": "反转正则表达式",
|
||||||
|
"note": "Note",
|
||||||
|
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "是否删除应用?",
|
"one": "是否删除应用?",
|
||||||
"other": "是否删除应用?"
|
"other": "是否删除应用?"
|
||||||
@@ -304,15 +341,15 @@
|
|||||||
"other": "清除了 {n} 个日志({before} 之前,{after} 之后)"
|
"other": "清除了 {n} 个日志({before} 之前,{after} 之后)"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesAvailable": {
|
"xAndNMoreUpdatesAvailable": {
|
||||||
"one": "{} 和另外 1 个应用可以更新了。",
|
"one": "“{}”和另外 1 个应用可以更新了。",
|
||||||
"other": "{} 和另外 {} 个应用可以更新了。"
|
"other": "“{}”和另外 {} 个应用可以更新了。"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesInstalled": {
|
"xAndNMoreUpdatesInstalled": {
|
||||||
"one": "{} 和另外 1 个应用已更新。",
|
"one": "{} 和另外 1 个应用已更新。",
|
||||||
"other": "{} 和另外 {} 个应用已更新。"
|
"other": "“{}”和另外 {} 个应用已更新。"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
"one": "{} 和另外 1 个应用已尝试更新。",
|
"one": "{} 和另外 1 个应用已尝试更新。",
|
||||||
"other": "{} 和另外 {} 个应用已尝试更新。"
|
"other": "“{}”和另外 {} 个应用已尝试更新。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
build.sh
@@ -7,8 +7,15 @@ trap "cd "$CURR_DIR"" EXIT
|
|||||||
if [ -z "$1" ]; then
|
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
|
git fetch && git merge origin/main && git push # Typically run after a PR to main, so bring dev up to date
|
||||||
fi
|
fi
|
||||||
rm ./build/app/outputs/flutter-apk/* 2>/dev/null # Get rid of older builds if any
|
cd .flutter
|
||||||
flutter build apk && flutter build apk --split-per-abi # Build (both split and combined APKs)
|
git fetch
|
||||||
|
git checkout "$(flutter --version | head -2 | tail -1 | awk '{print $4}')" # Ensure included Flutter submodule version equals my environment
|
||||||
|
cd ..
|
||||||
|
rm ./build/app/outputs/flutter-apk/* 2>/dev/null # Get rid of older builds if any
|
||||||
|
flutter build apk --flavor normal && flutter build apk --split-per-abi --flavor normal # Build (both split and combined APKs)
|
||||||
|
for file in ./build/app/outputs/flutter-apk/app-*normal*.apk*; do mv "$file" "${file//-normal/}"; done
|
||||||
|
flutter build apk --flavor fdroid -t lib/main_fdroid.dart && # Do the same for the F-Droid flavour
|
||||||
|
flutter build apk --split-per-abi --flavor fdroid -t lib/main_fdroid.dart
|
||||||
for file in ./build/app/outputs/flutter-apk/*.sha1; do gpg --sign --detach-sig "$file"; done # Generate PGP signatures
|
for file in ./build/app/outputs/flutter-apk/*.sha1; do gpg --sign --detach-sig "$file"; done # Generate PGP signatures
|
||||||
rsync -r ./build/app/outputs/flutter-apk/ ~/Downloads/Obtainium-build/ # Dropoff in Downloads to allow for drag-drop into Flatpak Firefox
|
rsync -r ./build/app/outputs/flutter-apk/ ~/Downloads/Obtainium-build/ # Dropoff in Downloads to allow for drag-drop into Flatpak Firefox
|
||||||
cd ~/Downloads/Obtainium-build/ # Make zips just in case (for in-comment uploads)
|
cd ~/Downloads/Obtainium-build/ # Make zips just in case (for in-comment uploads)
|
||||||
|
|||||||
54
fastlane/metadata/android/en-US/full_description.txt
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<p>Obtainium allows you to install and update Apps directly from their releases pages, and receive notifications when new releases are made available.</p>
|
||||||
|
<p>Read the <a href="https://github.com/ImranR98/Obtainium/wiki">Wiki</a></p>
|
||||||
|
<p>
|
||||||
|
<b>Currently supported App sources:</b>
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>Open Source - General:</p>
|
||||||
|
<ul>
|
||||||
|
<li>GitHub</li>
|
||||||
|
<li>GitLab</li>
|
||||||
|
<li>Codeberg</li>
|
||||||
|
<li>F-Droid</li>
|
||||||
|
<li>Third Party F-Droid Repos</li>
|
||||||
|
<li>IzzyOnDroid</li>
|
||||||
|
<li>SourceForge</li>
|
||||||
|
<li>SourceHut</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Other - General:</p>
|
||||||
|
<ul>
|
||||||
|
<li>APKPure</li>
|
||||||
|
<li>Aptoide</li>
|
||||||
|
<li>Uptodowng</li>
|
||||||
|
<li>APKMirror (Track-Only)</li>
|
||||||
|
<li>Huawei AppGallery</li>
|
||||||
|
<li>Jenkins Jobs</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Open Source - App-Specific:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Mullvad</li>
|
||||||
|
<li>Signal</li>
|
||||||
|
<li>VLC</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Other - App-Specific:</p>
|
||||||
|
<ul>
|
||||||
|
<li>WhatsApp</li>
|
||||||
|
<li>Telegram App</li>
|
||||||
|
<li>Neutron Code</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><p>"HTML" (Fallback): Any other URL that returns an HTML page with links to APK files</p></li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
<b>Limitations:</b>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For some sources, data is gathered using Web scraping and can easily break due to changes in website design. In such cases, more reliable methods may be unavailable.
|
||||||
|
</p>
|
||||||
BIN
fastlane/metadata/android/en-US/images/featureGraphic.png
Executable file
|
After Width: | Height: | Size: 66 KiB |
BIN
fastlane/metadata/android/en-US/images/icon.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
Normal file
|
After Width: | Height: | Size: 238 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/4.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/5.png
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/6.png
Normal file
|
After Width: | Height: | Size: 262 KiB |
1
fastlane/metadata/android/en-US/short_description.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Get android app updates directly from the source
|
||||||
1
fastlane/metadata/android/en-US/title.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Obtainium
|
||||||
@@ -5,17 +5,20 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class APKCombo extends AppSource {
|
class APKCombo extends AppSource {
|
||||||
APKCombo() {
|
APKCombo() {
|
||||||
host = 'apkcombo.com';
|
hosts = ['apkcombo.com'];
|
||||||
|
showReleaseDateAsVersionToggle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/+[^/]+/+[^/]+');
|
RegExp standardUrlRegEx = RegExp(
|
||||||
var match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+[^/]+',
|
||||||
|
caseSensitive: false);
|
||||||
|
var match = standardUrlRegEx.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -26,18 +29,19 @@ class APKCombo extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Map<String, String>?> getRequestHeaders(
|
Future<Map<String, String>?> getRequestHeaders(
|
||||||
{Map<String, dynamic> additionalSettings = const <String, dynamic>{},
|
Map<String, dynamic> additionalSettings,
|
||||||
bool forAPKDownload = false}) async {
|
{bool forAPKDownload = false}) async {
|
||||||
return {
|
return {
|
||||||
"User-Agent": "curl/8.0.1",
|
"User-Agent": "curl/8.0.1",
|
||||||
"Accept": "*/*",
|
"Accept": "*/*",
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"Host": "$host"
|
"Host": hosts[0]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<MapEntry<String, String>>> getApkUrls(String standardUrl) async {
|
Future<List<MapEntry<String, String>>> getApkUrls(
|
||||||
var res = await sourceRequest('$standardUrl/download/apk');
|
String standardUrl, Map<String, dynamic> additionalSettings) async {
|
||||||
|
var res = await sourceRequest('$standardUrl/download/apk', {});
|
||||||
if (res.statusCode != 200) {
|
if (res.statusCode != 200) {
|
||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
}
|
}
|
||||||
@@ -70,9 +74,9 @@ class APKCombo extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> apkUrlPrefetchModifier(
|
Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
|
||||||
String apkUrl, String standardUrl) async {
|
Map<String, dynamic> additionalSettings) async {
|
||||||
var freshURLs = await getApkUrls(standardUrl);
|
var freshURLs = await getApkUrls(standardUrl, additionalSettings);
|
||||||
var path2Match = Uri.parse(apkUrl).path;
|
var path2Match = Uri.parse(apkUrl).path;
|
||||||
for (var url in freshURLs) {
|
for (var url in freshURLs) {
|
||||||
if (Uri.parse(url.value).path == path2Match) {
|
if (Uri.parse(url.value).path == path2Match) {
|
||||||
@@ -88,7 +92,7 @@ class APKCombo extends AppSource {
|
|||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
String appId = (await tryInferringAppId(standardUrl))!;
|
String appId = (await tryInferringAppId(standardUrl))!;
|
||||||
var preres = await sourceRequest(standardUrl);
|
var preres = await sourceRequest(standardUrl, additionalSettings);
|
||||||
if (preres.statusCode != 200) {
|
if (preres.statusCode != 200) {
|
||||||
throw getObtainiumHttpError(preres);
|
throw getObtainiumHttpError(preres);
|
||||||
}
|
}
|
||||||
@@ -112,7 +116,9 @@ class APKCombo extends AppSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return APKDetails(
|
return APKDetails(
|
||||||
version, await getApkUrls(standardUrl), AppNames(author, appName),
|
version,
|
||||||
|
await getApkUrls(standardUrl, additionalSettings),
|
||||||
|
AppNames(author, appName),
|
||||||
releaseDate: releaseDate);
|
releaseDate: releaseDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class APKMirror extends AppSource {
|
class APKMirror extends AppSource {
|
||||||
APKMirror() {
|
APKMirror() {
|
||||||
host = 'apkmirror.com';
|
hosts = ['apkmirror.com'];
|
||||||
enforceTrackOnly = true;
|
enforceTrackOnly = true;
|
||||||
|
showReleaseDateAsVersionToggle = true;
|
||||||
|
|
||||||
additionalSourceAppSpecificSettingFormItems = [
|
additionalSourceAppSpecificSettingFormItems = [
|
||||||
[
|
[
|
||||||
@@ -32,12 +33,14 @@ class APKMirror extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/apk/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/apk/[^/]+/[^/]+',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -57,7 +60,7 @@ class APKMirror extends AppSource {
|
|||||||
true
|
true
|
||||||
? additionalSettings['filterReleaseTitlesByRegEx']
|
? additionalSettings['filterReleaseTitlesByRegEx']
|
||||||
: null;
|
: null;
|
||||||
Response res = await sourceRequest('$standardUrl/feed');
|
Response res = await sourceRequest('$standardUrl/feed', additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var items = parse(res.body).querySelectorAll('item');
|
var items = parse(res.body).querySelectorAll('item');
|
||||||
dynamic targetRelease;
|
dynamic targetRelease;
|
||||||
@@ -83,7 +86,7 @@ class APKMirror extends AppSource {
|
|||||||
dateString != null ? HttpDate.parse('$dateString GMT') : null;
|
dateString != null ? HttpDate.parse('$dateString GMT') : null;
|
||||||
String? version = titleString
|
String? version = titleString
|
||||||
?.substring(RegExp('[0-9]').firstMatch(titleString)?.start ?? 0,
|
?.substring(RegExp('[0-9]').firstMatch(titleString)?.start ?? 0,
|
||||||
RegExp(' by ').firstMatch(titleString)?.start ?? 0)
|
RegExp(' by ').allMatches(titleString).last.start)
|
||||||
.trim();
|
.trim();
|
||||||
if (version == null || version.isEmpty) {
|
if (version == null || version.isEmpty) {
|
||||||
version = titleString;
|
version = titleString;
|
||||||
|
|||||||
@@ -20,23 +20,29 @@ parseDateTimeMMMddCommayyyy(String? dateString) {
|
|||||||
|
|
||||||
class APKPure extends AppSource {
|
class APKPure extends AppSource {
|
||||||
APKPure() {
|
APKPure() {
|
||||||
host = 'apkpure.com';
|
hosts = ['apkpure.net', 'apkpure.com'];
|
||||||
allowSubDomains = true;
|
allowSubDomains = true;
|
||||||
|
naiveStandardVersionDetection = true;
|
||||||
|
showReleaseDateAsVersionToggle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegExB = RegExp('^https?://m.$host/+[^/]+/+[^/]+');
|
RegExp standardUrlRegExB = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
|
'^https?://m.${getSourceRegex(hosts)}/+[^/]+/+[^/]+(/+[^/]+)?',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegExB.firstMatch(url);
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
url = 'https://$host${Uri.parse(url).path}';
|
url = 'https://${getSourceRegex(hosts)}${Uri.parse(url).path}';
|
||||||
}
|
}
|
||||||
RegExp standardUrlRegExA = RegExp('^https?://$host/+[^/]+/+[^/]+');
|
RegExp standardUrlRegExA = RegExp(
|
||||||
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+[^/]+(/+[^/]+)?',
|
||||||
|
caseSensitive: false);
|
||||||
|
match = standardUrlRegExA.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -52,8 +58,8 @@ class APKPure extends AppSource {
|
|||||||
) async {
|
) async {
|
||||||
String appId = (await tryInferringAppId(standardUrl))!;
|
String appId = (await tryInferringAppId(standardUrl))!;
|
||||||
String host = Uri.parse(standardUrl).host;
|
String host = Uri.parse(standardUrl).host;
|
||||||
var res = await sourceRequest('$standardUrl/download');
|
var res = await sourceRequest('$standardUrl/download', additionalSettings);
|
||||||
var resChangelog = await sourceRequest(standardUrl);
|
var resChangelog = await sourceRequest(standardUrl, additionalSettings);
|
||||||
if (res.statusCode == 200 && resChangelog.statusCode == 200) {
|
if (res.statusCode == 200 && resChangelog.statusCode == 200) {
|
||||||
var html = parse(res.body);
|
var html = parse(res.body);
|
||||||
var htmlChangelog = parse(resChangelog.body);
|
var htmlChangelog = parse(resChangelog.body);
|
||||||
@@ -66,7 +72,8 @@ class APKPure extends AppSource {
|
|||||||
DateTime? releaseDate = parseDateTimeMMMddCommayyyy(dateString);
|
DateTime? releaseDate = parseDateTimeMMMddCommayyyy(dateString);
|
||||||
String type = html.querySelector('a.info-tag')?.text.trim() ?? 'APK';
|
String type = html.querySelector('a.info-tag')?.text.trim() ?? 'APK';
|
||||||
List<MapEntry<String, String>> apkUrls = [
|
List<MapEntry<String, String>> apkUrls = [
|
||||||
MapEntry('$appId.apk', 'https://d.$host/b/$type/$appId?version=latest')
|
MapEntry('$appId.apk',
|
||||||
|
'https://d.${hosts.contains(host) ? 'cdnpure.com' : host}/b/$type/$appId?version=latest')
|
||||||
];
|
];
|
||||||
String author = html
|
String author = html
|
||||||
.querySelector('span.info-sdk')
|
.querySelector('span.info-sdk')
|
||||||
|
|||||||
@@ -6,29 +6,35 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class Aptoide extends AppSource {
|
class Aptoide extends AppSource {
|
||||||
Aptoide() {
|
Aptoide() {
|
||||||
host = 'aptoide.com';
|
hosts = ['aptoide.com'];
|
||||||
name = tr('Aptoide');
|
name = 'Aptoide';
|
||||||
allowSubDomains = true;
|
allowSubDomains = true;
|
||||||
|
naiveStandardVersionDetection = true;
|
||||||
|
showReleaseDateAsVersionToggle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://([^\\.]+\\.){2,}$host');
|
RegExp standardUrlRegEx = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
'^https?://([^\\.]+\\.){2,}${getSourceRegex(hosts)}',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String?> tryInferringAppId(String standardUrl,
|
Future<String?> tryInferringAppId(String standardUrl,
|
||||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||||
return (await getAppDetailsJSON(standardUrl))['package'];
|
return (await getAppDetailsJSON(
|
||||||
|
standardUrl, additionalSettings))['package'];
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getAppDetailsJSON(String standardUrl) async {
|
Future<Map<String, dynamic>> getAppDetailsJSON(
|
||||||
var res = await sourceRequest(standardUrl);
|
String standardUrl, Map<String, dynamic> additionalSettings) async {
|
||||||
|
var res = await sourceRequest(standardUrl, additionalSettings);
|
||||||
if (res.statusCode != 200) {
|
if (res.statusCode != 200) {
|
||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
}
|
}
|
||||||
@@ -39,8 +45,8 @@ class Aptoide extends AppSource {
|
|||||||
} else {
|
} else {
|
||||||
throw NoReleasesError();
|
throw NoReleasesError();
|
||||||
}
|
}
|
||||||
var res2 =
|
var res2 = await sourceRequest(
|
||||||
await sourceRequest('https://ws2.aptoide.com/api/7/getApp/app_id/$id');
|
'https://ws2.aptoide.com/api/7/getApp/app_id/$id', additionalSettings);
|
||||||
if (res2.statusCode != 200) {
|
if (res2.statusCode != 200) {
|
||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
}
|
}
|
||||||
@@ -52,7 +58,7 @@ class Aptoide extends AppSource {
|
|||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
var appDetails = await getAppDetailsJSON(standardUrl);
|
var appDetails = await getAppDetailsJSON(standardUrl, additionalSettings);
|
||||||
String appName = appDetails['name'] ?? tr('app');
|
String appName = appDetails['name'] ?? tr('app');
|
||||||
String author = appDetails['developer']?['name'] ?? name;
|
String author = appDetails['developer']?['name'] ?? name;
|
||||||
String? dateStr = appDetails['updated'];
|
String? dateStr = appDetails['updated'];
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
class Codeberg extends AppSource {
|
class Codeberg extends AppSource {
|
||||||
GitHub gh = GitHub();
|
GitHub gh = GitHub();
|
||||||
Codeberg() {
|
Codeberg() {
|
||||||
host = 'codeberg.org';
|
hosts = ['codeberg.org'];
|
||||||
|
|
||||||
additionalSourceAppSpecificSettingFormItems =
|
additionalSourceAppSpecificSettingFormItems =
|
||||||
gh.additionalSourceAppSpecificSettingFormItems;
|
gh.additionalSourceAppSpecificSettingFormItems;
|
||||||
@@ -16,12 +16,14 @@ class Codeberg extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -35,7 +37,7 @@ class Codeberg extends AppSource {
|
|||||||
) async {
|
) async {
|
||||||
return await gh.getLatestAPKDetailsCommon2(standardUrl, additionalSettings,
|
return await gh.getLatestAPKDetailsCommon2(standardUrl, additionalSettings,
|
||||||
(bool useTagUrl) async {
|
(bool useTagUrl) async {
|
||||||
return 'https://$host/api/v1/repos${standardUrl.substring('https://$host'.length)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100';
|
return 'https://${hosts[0]}/api/v1/repos${standardUrl.substring('https://${hosts[0]}'.length)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100';
|
||||||
}, null);
|
}, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +52,7 @@ class Codeberg extends AppSource {
|
|||||||
{Map<String, dynamic> querySettings = const {}}) async {
|
{Map<String, dynamic> querySettings = const {}}) async {
|
||||||
return gh.searchCommon(
|
return gh.searchCommon(
|
||||||
query,
|
query,
|
||||||
'https://$host/api/v1/repos/search?q=${Uri.encodeQueryComponent(query)}&limit=100',
|
'https://${hosts[0]}/api/v1/repos/search?q=${Uri.encodeQueryComponent(query)}&limit=100',
|
||||||
'data',
|
'data',
|
||||||
querySettings: querySettings);
|
querySettings: querySettings);
|
||||||
}
|
}
|
||||||
|
|||||||
44
lib/app_sources/directAPKLink.dart
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:obtainium/app_sources/html.dart';
|
||||||
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
|
|
||||||
|
class DirectAPKLink extends AppSource {
|
||||||
|
HTML html = HTML();
|
||||||
|
|
||||||
|
DirectAPKLink() {
|
||||||
|
neverAutoSelect = true;
|
||||||
|
name = tr('directAPKLink');
|
||||||
|
additionalSourceAppSpecificSettingFormItems = html
|
||||||
|
.additionalSourceAppSpecificSettingFormItems
|
||||||
|
.where((element) => element
|
||||||
|
.where((element) => element.key == 'requestHeader')
|
||||||
|
.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
excludeCommonSettingKeys = [
|
||||||
|
'versionExtractionRegEx',
|
||||||
|
'matchGroupToUse',
|
||||||
|
'versionDetection',
|
||||||
|
'useVersionCodeAsOSVersion',
|
||||||
|
'apkFilterRegEx',
|
||||||
|
'autoApkFilterByArch'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<APKDetails> getLatestAPKDetails(
|
||||||
|
String standardUrl,
|
||||||
|
Map<String, dynamic> additionalSettings,
|
||||||
|
) async {
|
||||||
|
var additionalSettingsNew =
|
||||||
|
getDefaultValuesFromFormItems(html.combinedAppSpecificSettingFormItems);
|
||||||
|
for (var s in additionalSettings.keys) {
|
||||||
|
if (additionalSettingsNew.containsKey(s)) {
|
||||||
|
additionalSettingsNew[s] = additionalSettings[s];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
additionalSettingsNew['defaultPseudoVersioningMethod'] = 'partialAPKHash';
|
||||||
|
additionalSettingsNew['directAPKLink'] = true;
|
||||||
|
additionalSettings['versionDetection'] = false;
|
||||||
|
return html.getLatestAPKDetails(standardUrl, additionalSettingsNew);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,14 +3,17 @@ import 'dart:convert';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
|
import 'package:obtainium/app_sources/github.dart';
|
||||||
|
import 'package:obtainium/app_sources/gitlab.dart';
|
||||||
import 'package:obtainium/components/generated_form.dart';
|
import 'package:obtainium/components/generated_form.dart';
|
||||||
import 'package:obtainium/custom_errors.dart';
|
import 'package:obtainium/custom_errors.dart';
|
||||||
import 'package:obtainium/providers/source_provider.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
|
|
||||||
class FDroid extends AppSource {
|
class FDroid extends AppSource {
|
||||||
FDroid() {
|
FDroid() {
|
||||||
host = 'f-droid.org';
|
hosts = ['f-droid.org'];
|
||||||
name = tr('fdroid');
|
name = tr('fdroid');
|
||||||
|
naiveStandardVersionDetection = true;
|
||||||
canSearch = true;
|
canSearch = true;
|
||||||
additionalSourceAppSpecificSettingFormItems = [
|
additionalSourceAppSpecificSettingFormItems = [
|
||||||
[
|
[
|
||||||
@@ -36,19 +39,22 @@ class FDroid extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegExB =
|
RegExp standardUrlRegExB = RegExp(
|
||||||
RegExp('^https?://$host/+[^/]+/+packages/+[^/]+');
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+packages/+[^/]+',
|
||||||
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegExB.firstMatch(url);
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
url =
|
url =
|
||||||
'https://${Uri.parse(url.substring(0, match.end)).host}/packages/${Uri.parse(url).pathSegments.last}';
|
'https://${Uri.parse(match.group(0)!).host}/packages/${Uri.parse(url).pathSegments.last}';
|
||||||
}
|
}
|
||||||
RegExp standardUrlRegExA = RegExp('^https?://$host/+packages/+[^/]+');
|
RegExp standardUrlRegExA = RegExp(
|
||||||
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/+packages/+[^/]+',
|
||||||
|
caseSensitive: false);
|
||||||
|
match = standardUrlRegExA.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -64,8 +70,9 @@ class FDroid extends AppSource {
|
|||||||
) async {
|
) async {
|
||||||
String? appId = await tryInferringAppId(standardUrl);
|
String? appId = await tryInferringAppId(standardUrl);
|
||||||
String host = Uri.parse(standardUrl).host;
|
String host = Uri.parse(standardUrl).host;
|
||||||
return getAPKUrlsFromFDroidPackagesAPIResponse(
|
var details = getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||||
await sourceRequest('https://$host/api/v1/packages/$appId'),
|
await sourceRequest(
|
||||||
|
'https://$host/api/v1/packages/$appId', additionalSettings),
|
||||||
'https://$host/repo/$appId',
|
'https://$host/repo/$appId',
|
||||||
standardUrl,
|
standardUrl,
|
||||||
name,
|
name,
|
||||||
@@ -79,13 +86,59 @@ class FDroid extends AppSource {
|
|||||||
true
|
true
|
||||||
? additionalSettings['filterVersionsByRegEx']
|
? additionalSettings['filterVersionsByRegEx']
|
||||||
: null);
|
: null);
|
||||||
|
if (!hostChanged) {
|
||||||
|
try {
|
||||||
|
var res = await sourceRequest(
|
||||||
|
'https://gitlab.com/fdroid/fdroiddata/-/raw/master/metadata/$appId.yml',
|
||||||
|
additionalSettings);
|
||||||
|
var lines = res.body.split('\n');
|
||||||
|
var authorLines = lines.where((l) => l.startsWith('AuthorName: '));
|
||||||
|
if (authorLines.isNotEmpty) {
|
||||||
|
details.names.author =
|
||||||
|
authorLines.first.split(': ').sublist(1).join(': ');
|
||||||
|
}
|
||||||
|
var changelogUrls = lines
|
||||||
|
.where((l) => l.startsWith('Changelog: '))
|
||||||
|
.map((e) => e.split(' ').sublist(1).join(' '));
|
||||||
|
if (changelogUrls.isNotEmpty) {
|
||||||
|
details.changeLog = changelogUrls.first;
|
||||||
|
bool isGitHub = false;
|
||||||
|
bool isGitLab = false;
|
||||||
|
try {
|
||||||
|
GitHub().sourceSpecificStandardizeURL(details.changeLog!);
|
||||||
|
isGitHub = true;
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
GitLab().sourceSpecificStandardizeURL(details.changeLog!);
|
||||||
|
isGitLab = true;
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
if ((isGitHub || isGitLab) &&
|
||||||
|
(details.changeLog?.indexOf('/blob/') ?? -1) >= 0) {
|
||||||
|
details.changeLog = (await sourceRequest(
|
||||||
|
details.changeLog!.replaceFirst('/blob/', '/raw/'),
|
||||||
|
additionalSettings))
|
||||||
|
.body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Fail silently
|
||||||
|
}
|
||||||
|
if ((details.changeLog?.length ?? 0) > 2048) {
|
||||||
|
details.changeLog = '${details.changeLog!.substring(0, 2048)}...';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return details;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Map<String, List<String>>> search(String query,
|
Future<Map<String, List<String>>> search(String query,
|
||||||
{Map<String, dynamic> querySettings = const {}}) async {
|
{Map<String, dynamic> querySettings = const {}}) async {
|
||||||
Response res = await sourceRequest(
|
Response res = await sourceRequest(
|
||||||
'https://search.$host/?q=${Uri.encodeQueryComponent(query)}');
|
'https://search.${hosts[0]}/?q=${Uri.encodeQueryComponent(query)}', {});
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
Map<String, List<String>> urlsWithDescriptions = {};
|
Map<String, List<String>> urlsWithDescriptions = {};
|
||||||
parse(res.body).querySelectorAll('.package-header').forEach((e) {
|
parse(res.body).querySelectorAll('.package-header').forEach((e) {
|
||||||
@@ -110,79 +163,79 @@ class FDroid extends AppSource {
|
|||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
|
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||||
Response res, String apkUrlPrefix, String standardUrl, String sourceName,
|
Response res, String apkUrlPrefix, String standardUrl, String sourceName,
|
||||||
{bool autoSelectHighestVersionCode = false,
|
{bool autoSelectHighestVersionCode = false,
|
||||||
bool trySelectingSuggestedVersionCode = false,
|
bool trySelectingSuggestedVersionCode = false,
|
||||||
String? filterVersionsByRegEx}) {
|
String? filterVersionsByRegEx}) {
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var response = jsonDecode(res.body);
|
var response = jsonDecode(res.body);
|
||||||
List<dynamic> releases = response['packages'] ?? [];
|
List<dynamic> releases = response['packages'] ?? [];
|
||||||
if (releases.isEmpty) {
|
if (releases.isEmpty) {
|
||||||
throw NoReleasesError();
|
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'];
|
|
||||||
}
|
}
|
||||||
}
|
String? version;
|
||||||
// Apply the release filter if any
|
Iterable<dynamic> releaseChoices = [];
|
||||||
if (filterVersionsByRegEx != null) {
|
// Grab the versionCode suggested if the user chose to do that
|
||||||
version = null;
|
// Only do so at this stage if the user has no release filter
|
||||||
releaseChoices = [];
|
if (trySelectingSuggestedVersionCode &&
|
||||||
for (var i = 0; i < releases.length; i++) {
|
response['suggestedVersionCode'] != null &&
|
||||||
if (RegExp(filterVersionsByRegEx)
|
filterVersionsByRegEx == null) {
|
||||||
.hasMatch(releases[i]['versionName'])) {
|
var suggestedReleases = releases.where((element) =>
|
||||||
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']);
|
element['versionCode'] == response['suggestedVersionCode']);
|
||||||
if (suggestedReleases.isNotEmpty) {
|
if (suggestedReleases.isNotEmpty) {
|
||||||
releaseChoices = suggestedReleases;
|
releaseChoices = suggestedReleases;
|
||||||
|
version = suggestedReleases.first['versionName'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Apply the release filter if any
|
||||||
|
if (filterVersionsByRegEx?.isNotEmpty == true) {
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
if (releaseChoices.isEmpty) {
|
|
||||||
throw NoReleasesError();
|
|
||||||
}
|
|
||||||
List<String> apkUrls = releaseChoices
|
|
||||||
.map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
|
|
||||||
.toList();
|
|
||||||
return APKDetails(version, getApkUrlsFromUrls(apkUrls),
|
|
||||||
AppNames(sourceName, Uri.parse(standardUrl).pathSegments.last));
|
|
||||||
} else {
|
|
||||||
throw getObtainiumHttpError(res);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
class FDroidRepo extends AppSource {
|
class FDroidRepo extends AppSource {
|
||||||
FDroidRepo() {
|
FDroidRepo() {
|
||||||
name = tr('fdroidThirdPartyRepo');
|
name = tr('fdroidThirdPartyRepo');
|
||||||
|
canSearch = true;
|
||||||
|
excludeFromMassSearch = true;
|
||||||
|
neverAutoSelect = true;
|
||||||
|
showReleaseDateAsVersionToggle = true;
|
||||||
|
|
||||||
additionalSourceAppSpecificSettingFormItems = [
|
additionalSourceAppSpecificSettingFormItems = [
|
||||||
[
|
[
|
||||||
@@ -22,17 +26,100 @@ 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 {
|
||||||
|
String? url = querySettings['url'];
|
||||||
|
if (url == null) {
|
||||||
|
throw NoReleasesError();
|
||||||
|
}
|
||||||
|
url = removeQueryParamsFromUrl(standardizeUrl(url));
|
||||||
|
var res = await sourceRequest('$url/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']!;
|
||||||
|
String appName = app.querySelector('name')?.innerHtml ?? appId;
|
||||||
|
String appDesc = app.querySelector('desc')?.innerHtml ?? '';
|
||||||
|
if (query.isEmpty ||
|
||||||
|
appId.contains(query) ||
|
||||||
|
appName.contains(query) ||
|
||||||
|
appDesc.contains(query)) {
|
||||||
|
results['$url?appId=$appId'] = [appName, appDesc];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
} else {
|
||||||
|
throw getObtainiumHttpError(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
App endOfGetAppChanges(App app) {
|
||||||
|
var uri = Uri.parse(app.url);
|
||||||
|
String? appId;
|
||||||
|
if (!isTempId(app)) {
|
||||||
|
appId = app.id;
|
||||||
|
} else if (uri.queryParameters['appId'] != null) {
|
||||||
|
appId = uri.queryParameters['appId'];
|
||||||
|
}
|
||||||
|
if (appId != null) {
|
||||||
|
app.url = uri
|
||||||
|
.replace(
|
||||||
|
queryParameters: Map.fromEntries(
|
||||||
|
[...uri.queryParameters.entries, MapEntry('appId', appId)]))
|
||||||
|
.toString();
|
||||||
|
app.additionalSettings['appIdOrName'] = appId;
|
||||||
|
app.id = appId;
|
||||||
|
}
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<APKDetails> getLatestAPKDetails(
|
Future<APKDetails> getLatestAPKDetails(
|
||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
String? appIdOrName = additionalSettings['appIdOrName'];
|
String? appIdOrName = additionalSettings['appIdOrName'];
|
||||||
|
var standardUri = Uri.parse(standardUrl);
|
||||||
|
if (standardUri.queryParameters['appId'] != null) {
|
||||||
|
appIdOrName = standardUri.queryParameters['appId'];
|
||||||
|
}
|
||||||
|
standardUrl = removeQueryParamsFromUrl(standardUrl);
|
||||||
bool pickHighestVersionCode = additionalSettings['pickHighestVersionCode'];
|
bool pickHighestVersionCode = additionalSettings['pickHighestVersionCode'];
|
||||||
if (appIdOrName == null) {
|
if (appIdOrName == null) {
|
||||||
throw NoReleasesError();
|
throw NoReleasesError();
|
||||||
}
|
}
|
||||||
var res = await sourceRequest('$standardUrl/index.xml');
|
var res = await sourceRequest(
|
||||||
|
'$standardUrl${standardUrl.endsWith('/index.xml') ? '' : '/index.xml'}',
|
||||||
|
additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var body = parse(res.body);
|
var body = parse(res.body);
|
||||||
var foundApps = body.querySelectorAll('application').where((element) {
|
var foundApps = body.querySelectorAll('application').where((element) {
|
||||||
@@ -41,7 +128,7 @@ class FDroidRepo extends AppSource {
|
|||||||
if (foundApps.isEmpty) {
|
if (foundApps.isEmpty) {
|
||||||
foundApps = body.querySelectorAll('application').where((element) {
|
foundApps = body.querySelectorAll('application').where((element) {
|
||||||
return element.querySelector('name')?.innerHtml.toLowerCase() ==
|
return element.querySelector('name')?.innerHtml.toLowerCase() ==
|
||||||
appIdOrName.toLowerCase();
|
appIdOrName!.toLowerCase();
|
||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
if (foundApps.isEmpty) {
|
if (foundApps.isEmpty) {
|
||||||
@@ -50,7 +137,7 @@ class FDroidRepo extends AppSource {
|
|||||||
.querySelector('name')
|
.querySelector('name')
|
||||||
?.innerHtml
|
?.innerHtml
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.contains(appIdOrName.toLowerCase()) ??
|
.contains(appIdOrName!.toLowerCase()) ??
|
||||||
false;
|
false;
|
||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
@@ -58,8 +145,9 @@ class FDroidRepo extends AppSource {
|
|||||||
throw ObtainiumError(tr('appWithIdOrNameNotFound'));
|
throw ObtainiumError(tr('appWithIdOrNameNotFound'));
|
||||||
}
|
}
|
||||||
var authorName = body.querySelector('repo')?.attributes['name'] ?? name;
|
var authorName = body.querySelector('repo')?.attributes['name'] ?? name;
|
||||||
var appName =
|
String appId = foundApps[0].attributes['id']!;
|
||||||
foundApps[0].querySelector('name')?.innerHtml ?? appIdOrName;
|
foundApps[0].querySelector('name')?.innerHtml ?? appId;
|
||||||
|
var appName = foundApps[0].querySelector('name')?.innerHtml ?? appId;
|
||||||
var releases = foundApps[0].querySelectorAll('package');
|
var releases = foundApps[0].querySelectorAll('package');
|
||||||
String? latestVersion = releases[0].querySelector('version')?.innerHtml;
|
String? latestVersion = releases[0].querySelector('version')?.innerHtml;
|
||||||
String? added = releases[0].querySelector('added')?.innerHtml;
|
String? added = releases[0].querySelector('added')?.innerHtml;
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ import 'package:url_launcher/url_launcher_string.dart';
|
|||||||
|
|
||||||
class GitHub extends AppSource {
|
class GitHub extends AppSource {
|
||||||
GitHub() {
|
GitHub() {
|
||||||
host = 'github.com';
|
hosts = ['github.com'];
|
||||||
appIdInferIsOptional = true;
|
appIdInferIsOptional = true;
|
||||||
|
showReleaseDateAsVersionToggle = true;
|
||||||
|
|
||||||
sourceConfigSettingFormItems = [
|
sourceConfigSettingFormItems = [
|
||||||
GeneratedFormTextField('github-creds',
|
GeneratedFormTextField('github-creds',
|
||||||
@@ -76,6 +77,10 @@ class GitHub extends AppSource {
|
|||||||
[
|
[
|
||||||
GeneratedFormSwitch('dontSortReleasesList',
|
GeneratedFormSwitch('dontSortReleasesList',
|
||||||
label: tr('dontSortReleasesList'))
|
label: tr('dontSortReleasesList'))
|
||||||
|
],
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch('useLatestAssetDateAsReleaseDate',
|
||||||
|
label: tr('useLatestAssetDateAsReleaseDate'), defaultValue: false)
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -108,7 +113,8 @@ class GitHub extends AppSource {
|
|||||||
for (var path in possibleBuildGradleLocations) {
|
for (var path in possibleBuildGradleLocations) {
|
||||||
try {
|
try {
|
||||||
var res = await sourceRequest(
|
var res = await sourceRequest(
|
||||||
'${await convertStandardUrlToAPIUrl(standardUrl, additionalSettings)}/contents/$path');
|
'${await convertStandardUrlToAPIUrl(standardUrl, additionalSettings)}/contents/$path',
|
||||||
|
additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
try {
|
try {
|
||||||
var body = jsonDecode(res.body);
|
var body = jsonDecode(res.body);
|
||||||
@@ -117,22 +123,23 @@ class GitHub extends AppSource {
|
|||||||
.decode(body['content'].toString().split('\n').join('')))
|
.decode(body['content'].toString().split('\n').join('')))
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map((e) => e.trim());
|
.map((e) => e.trim());
|
||||||
var appId = trimmedLines
|
var appIds = trimmedLines.where((l) =>
|
||||||
.where((l) =>
|
l.startsWith('applicationId "') ||
|
||||||
l.startsWith('applicationId "') ||
|
l.startsWith('applicationId \''));
|
||||||
l.startsWith('applicationId \''))
|
appIds = appIds.map((appId) => appId
|
||||||
.first;
|
.split(appId.startsWith('applicationId "') ? '"' : '\'')[1]);
|
||||||
appId = appId
|
appIds = appIds.map((appId) {
|
||||||
.split(appId.startsWith('applicationId "') ? '"' : '\'')[1];
|
if (appId.startsWith('\${') && appId.endsWith('}')) {
|
||||||
if (appId.startsWith('\${') && appId.endsWith('}')) {
|
appId = trimmedLines
|
||||||
appId = trimmedLines
|
.where((l) => l.startsWith(
|
||||||
.where((l) => l.startsWith(
|
'def ${appId.substring(2, appId.length - 1)}'))
|
||||||
'def ${appId.substring(2, appId.length - 1)}'))
|
.first;
|
||||||
.first;
|
appId = appId.split(appId.contains('"') ? '"' : '\'')[1];
|
||||||
appId = appId.split(appId.contains('"') ? '"' : '\'')[1];
|
}
|
||||||
}
|
|
||||||
if (appId.isNotEmpty) {
|
|
||||||
return appId;
|
return appId;
|
||||||
|
}).where((appId) => appId.isNotEmpty);
|
||||||
|
if (appIds.length == 1) {
|
||||||
|
return appIds.first;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
LogsProvider().add(
|
LogsProvider().add(
|
||||||
@@ -148,18 +155,20 @@ class GitHub extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Map<String, String>?> getRequestHeaders(
|
Future<Map<String, String>?> getRequestHeaders(
|
||||||
{Map<String, dynamic> additionalSettings = const <String, dynamic>{},
|
Map<String, dynamic> additionalSettings,
|
||||||
bool forAPKDownload = false}) async {
|
{bool forAPKDownload = false}) async {
|
||||||
var token = await getTokenIfAny(additionalSettings);
|
var token = await getTokenIfAny(additionalSettings);
|
||||||
var headers = <String, String>{};
|
var headers = <String, String>{};
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
@@ -202,11 +211,11 @@ class GitHub extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getAPIHost(Map<String, dynamic> additionalSettings) async =>
|
Future<String> getAPIHost(Map<String, dynamic> additionalSettings) async =>
|
||||||
'https://api.$host';
|
'https://api.${hosts[0]}';
|
||||||
|
|
||||||
Future<String> convertStandardUrlToAPIUrl(
|
Future<String> convertStandardUrlToAPIUrl(
|
||||||
String standardUrl, Map<String, dynamic> additionalSettings) async =>
|
String standardUrl, Map<String, dynamic> additionalSettings) async =>
|
||||||
'${await getAPIHost(additionalSettings)}/repos${standardUrl.substring('https://$host'.length)}';
|
'${await getAPIHost(additionalSettings)}/repos${standardUrl.substring('https://${hosts[0]}'.length)}';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? changeLogPageFromStandardUrl(String standardUrl) =>
|
String? changeLogPageFromStandardUrl(String standardUrl) =>
|
||||||
@@ -233,23 +242,34 @@ class GitHub extends AppSource {
|
|||||||
bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true;
|
bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true;
|
||||||
bool dontSortReleasesList =
|
bool dontSortReleasesList =
|
||||||
additionalSettings['dontSortReleasesList'] == true;
|
additionalSettings['dontSortReleasesList'] == true;
|
||||||
String? latestTag;
|
bool useLatestAssetDateAsReleaseDate =
|
||||||
|
additionalSettings['useLatestAssetDateAsReleaseDate'] == true;
|
||||||
|
dynamic latestRelease;
|
||||||
if (verifyLatestTag) {
|
if (verifyLatestTag) {
|
||||||
var temp = requestUrl.split('?');
|
var temp = requestUrl.split('?');
|
||||||
Response res = await sourceRequest(
|
Response res = await sourceRequest(
|
||||||
'${temp[0]}/latest${temp.length > 1 ? '?${temp.sublist(1).join('?')}' : ''}');
|
'${temp[0]}/latest${temp.length > 1 ? '?${temp.sublist(1).join('?')}' : ''}',
|
||||||
|
additionalSettings);
|
||||||
if (res.statusCode != 200) {
|
if (res.statusCode != 200) {
|
||||||
if (onHttpErrorCode != null) {
|
if (onHttpErrorCode != null) {
|
||||||
onHttpErrorCode(res);
|
onHttpErrorCode(res);
|
||||||
}
|
}
|
||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
}
|
}
|
||||||
var jsres = jsonDecode(res.body);
|
latestRelease = jsonDecode(res.body);
|
||||||
latestTag = jsres['tag_name'] ?? jsres['name'];
|
|
||||||
}
|
}
|
||||||
Response res = await sourceRequest(requestUrl);
|
Response res = await sourceRequest(requestUrl, additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var releases = jsonDecode(res.body) as List<dynamic>;
|
var releases = jsonDecode(res.body) as List<dynamic>;
|
||||||
|
if (latestRelease != null) {
|
||||||
|
var latestTag = latestRelease['tag_name'] ?? latestRelease['name'];
|
||||||
|
if (releases
|
||||||
|
.where((element) =>
|
||||||
|
(element['tag_name'] ?? element['name']) == latestTag)
|
||||||
|
.isEmpty) {
|
||||||
|
releases = [latestRelease, ...releases];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<MapEntry<String, String>> getReleaseAPKUrls(dynamic release) =>
|
List<MapEntry<String, String>> getReleaseAPKUrls(dynamic release) =>
|
||||||
(release['assets'] as List<dynamic>?)
|
(release['assets'] as List<dynamic>?)
|
||||||
@@ -264,10 +284,31 @@ class GitHub extends AppSource {
|
|||||||
.toList() ??
|
.toList() ??
|
||||||
[];
|
[];
|
||||||
|
|
||||||
DateTime? getReleaseDateFromRelease(dynamic rel) =>
|
DateTime? getPublishDateFromRelease(dynamic rel) =>
|
||||||
rel?['published_at'] != null
|
rel?['published_at'] != null
|
||||||
? DateTime.parse(rel['published_at'])
|
? DateTime.parse(rel['published_at'])
|
||||||
: null;
|
: null;
|
||||||
|
DateTime? getNewestAssetDateFromRelease(dynamic rel) {
|
||||||
|
var t = (rel['assets'] as List<dynamic>?)
|
||||||
|
?.map((e) {
|
||||||
|
return e?['updated_at'] != null
|
||||||
|
? DateTime.parse(e['updated_at'])
|
||||||
|
: null;
|
||||||
|
})
|
||||||
|
.where((e) => e != null)
|
||||||
|
.toList();
|
||||||
|
t?.sort((a, b) => b!.compareTo(a!));
|
||||||
|
if (t?.isNotEmpty == true) {
|
||||||
|
return t!.first;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime? getReleaseDateFromRelease(dynamic rel, bool useAssetDate) =>
|
||||||
|
!useAssetDate
|
||||||
|
? getPublishDateFromRelease(rel)
|
||||||
|
: getNewestAssetDateFromRelease(rel);
|
||||||
|
|
||||||
if (dontSortReleasesList) {
|
if (dontSortReleasesList) {
|
||||||
releases = releases.reversed.toList();
|
releases = releases.reversed.toList();
|
||||||
} else {
|
} else {
|
||||||
@@ -292,19 +333,23 @@ class GitHub extends AppSource {
|
|||||||
(nameA as String).substring(matchA!.start, matchA.end),
|
(nameA as String).substring(matchA!.start, matchA.end),
|
||||||
(nameB as String).substring(matchB!.start, matchB.end));
|
(nameB as String).substring(matchB!.start, matchB.end));
|
||||||
} else {
|
} else {
|
||||||
return (getReleaseDateFromRelease(a) ?? DateTime(1))
|
return (getReleaseDateFromRelease(
|
||||||
.compareTo(getReleaseDateFromRelease(b) ?? DateTime(0));
|
a, useLatestAssetDateAsReleaseDate) ??
|
||||||
|
DateTime(1))
|
||||||
|
.compareTo(getReleaseDateFromRelease(
|
||||||
|
b, useLatestAssetDateAsReleaseDate) ??
|
||||||
|
DateTime(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (latestTag != null &&
|
if (latestRelease != null &&
|
||||||
releases.isNotEmpty &&
|
releases.isNotEmpty &&
|
||||||
latestTag !=
|
latestRelease !=
|
||||||
(releases[releases.length - 1]['tag_name'] ??
|
(releases[releases.length - 1]['tag_name'] ??
|
||||||
releases[0]['name'])) {
|
releases[0]['name'])) {
|
||||||
var ind = releases.indexWhere(
|
var ind = releases.indexWhere((element) =>
|
||||||
(element) => latestTag == (element['tag_name'] ?? element['name']));
|
latestRelease == (element['tag_name'] ?? element['name']));
|
||||||
if (ind >= 0) {
|
if (ind >= 0) {
|
||||||
releases.add(releases.removeAt(ind));
|
releases.add(releases.removeAt(ind));
|
||||||
}
|
}
|
||||||
@@ -337,6 +382,8 @@ class GitHub extends AppSource {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var apkUrls = getReleaseAPKUrls(releases[i]);
|
var apkUrls = getReleaseAPKUrls(releases[i]);
|
||||||
|
apkUrls = filterApks(apkUrls, additionalSettings['apkFilterRegEx'],
|
||||||
|
additionalSettings['invertAPKFilter']);
|
||||||
if (apkUrls.isEmpty && additionalSettings['trackOnly'] != true) {
|
if (apkUrls.isEmpty && additionalSettings['trackOnly'] != true) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -348,7 +395,8 @@ class GitHub extends AppSource {
|
|||||||
throw NoReleasesError();
|
throw NoReleasesError();
|
||||||
}
|
}
|
||||||
String? version = targetRelease['tag_name'] ?? targetRelease['name'];
|
String? version = targetRelease['tag_name'] ?? targetRelease['name'];
|
||||||
DateTime? releaseDate = getReleaseDateFromRelease(targetRelease);
|
DateTime? releaseDate = getReleaseDateFromRelease(
|
||||||
|
targetRelease, useLatestAssetDateAsReleaseDate);
|
||||||
if (version == null) {
|
if (version == null) {
|
||||||
throw NoVersionError();
|
throw NoVersionError();
|
||||||
}
|
}
|
||||||
@@ -410,7 +458,7 @@ class GitHub extends AppSource {
|
|||||||
String query, String requestUrl, String rootProp,
|
String query, String requestUrl, String rootProp,
|
||||||
{Function(Response)? onHttpErrorCode,
|
{Function(Response)? onHttpErrorCode,
|
||||||
Map<String, dynamic> querySettings = const {}}) async {
|
Map<String, dynamic> querySettings = const {}}) async {
|
||||||
Response res = await sourceRequest(requestUrl);
|
Response res = await sourceRequest(requestUrl, {});
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
int minStarCount = querySettings['minStarCount'] != null
|
int minStarCount = querySettings['minStarCount'] != null
|
||||||
? int.parse(querySettings['minStarCount'])
|
? int.parse(querySettings['minStarCount'])
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ import 'package:url_launcher/url_launcher_string.dart';
|
|||||||
|
|
||||||
class GitLab extends AppSource {
|
class GitLab extends AppSource {
|
||||||
GitLab() {
|
GitLab() {
|
||||||
host = 'gitlab.com';
|
hosts = ['gitlab.com'];
|
||||||
canSearch = true;
|
canSearch = true;
|
||||||
|
showReleaseDateAsVersionToggle = true;
|
||||||
|
|
||||||
sourceConfigSettingFormItems = [
|
sourceConfigSettingFormItems = [
|
||||||
GeneratedFormTextField('gitlab-creds',
|
GeneratedFormTextField('gitlab-creds',
|
||||||
@@ -52,12 +53,14 @@ class GitLab extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> getPATIfAny(Map<String, dynamic> additionalSettings) async {
|
Future<String?> getPATIfAny(Map<String, dynamic> additionalSettings) async {
|
||||||
@@ -80,20 +83,16 @@ class GitLab extends AppSource {
|
|||||||
@override
|
@override
|
||||||
Future<Map<String, List<String>>> search(String query,
|
Future<Map<String, List<String>>> search(String query,
|
||||||
{Map<String, dynamic> querySettings = const {}}) async {
|
{Map<String, dynamic> querySettings = const {}}) async {
|
||||||
String? PAT = await getPATIfAny({});
|
|
||||||
if (PAT == null) {
|
|
||||||
throw CredsNeededError(name);
|
|
||||||
}
|
|
||||||
var url =
|
var url =
|
||||||
'https://$host/api/v4/search?private_token=$PAT&scope=projects&search=${Uri.encodeQueryComponent(query)}';
|
'https://${hosts[0]}/api/v4/projects?search=${Uri.encodeQueryComponent(query)}';
|
||||||
var res = await sourceRequest(url);
|
var res = await sourceRequest(url, {});
|
||||||
if (res.statusCode != 200) {
|
if (res.statusCode != 200) {
|
||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
}
|
}
|
||||||
var json = jsonDecode(res.body) as List<dynamic>;
|
var json = jsonDecode(res.body) as List<dynamic>;
|
||||||
Map<String, List<String>> results = {};
|
Map<String, List<String>> results = {};
|
||||||
for (var element in json) {
|
for (var element in json) {
|
||||||
results['https://$host/${element['path_with_namespace']}'] = [
|
results['https://${hosts[0]}/${element['path_with_namespace']}'] = [
|
||||||
element['name_with_namespace'],
|
element['name_with_namespace'],
|
||||||
element['description'] ?? tr('noDescription')
|
element['description'] ?? tr('noDescription')
|
||||||
];
|
];
|
||||||
@@ -117,7 +116,8 @@ class GitLab extends AppSource {
|
|||||||
if (PAT != null) {
|
if (PAT != null) {
|
||||||
var names = GitHub().getAppNames(standardUrl);
|
var names = GitHub().getAppNames(standardUrl);
|
||||||
Response res = await sourceRequest(
|
Response res = await sourceRequest(
|
||||||
'https://$host/api/v4/projects/${names.author}%2F${names.name}/releases?private_token=$PAT');
|
'https://${hosts[0]}/api/v4/projects/${names.author}%2F${names.name}/releases?private_token=$PAT',
|
||||||
|
additionalSettings);
|
||||||
if (res.statusCode != 200) {
|
if (res.statusCode != 200) {
|
||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
}
|
}
|
||||||
@@ -152,7 +152,8 @@ class GitLab extends AppSource {
|
|||||||
releaseDate: releaseDate);
|
releaseDate: releaseDate);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Response res = await sourceRequest('$standardUrl/-/tags?format=atom');
|
Response res = await sourceRequest(
|
||||||
|
'$standardUrl/-/tags?format=atom', additionalSettings);
|
||||||
if (res.statusCode != 200) {
|
if (res.statusCode != 200) {
|
||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
}
|
}
|
||||||
@@ -174,7 +175,6 @@ class GitLab extends AppSource {
|
|||||||
...getLinksFromParsedHTML(entryContent,
|
...getLinksFromParsedHTML(entryContent,
|
||||||
RegExp('/[^/]+\\.apk\$', caseSensitive: false), '')
|
RegExp('/[^/]+\\.apk\$', caseSensitive: false), '')
|
||||||
.where((element) => Uri.parse(element).host != '')
|
.where((element) => Uri.parse(element).host != '')
|
||||||
.toList()
|
|
||||||
];
|
];
|
||||||
var entryId = entry.querySelector('id')?.innerHtml;
|
var entryId = entry.querySelector('id')?.innerHtml;
|
||||||
var version =
|
var version =
|
||||||
@@ -192,7 +192,7 @@ class GitLab extends AppSource {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (apkDetailsList.isEmpty) {
|
if (apkDetailsList.isEmpty) {
|
||||||
throw NoReleasesError();
|
throw NoReleasesError(note: tr('gitlabSourceNote'));
|
||||||
}
|
}
|
||||||
if (fallbackToOlderReleases) {
|
if (fallbackToOlderReleases) {
|
||||||
if (additionalSettings['trackOnly'] != true) {
|
if (additionalSettings['trackOnly'] != true) {
|
||||||
@@ -200,7 +200,7 @@ class GitLab extends AppSource {
|
|||||||
apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
|
apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
|
||||||
}
|
}
|
||||||
if (apkDetailsList.isEmpty) {
|
if (apkDetailsList.isEmpty) {
|
||||||
throw NoReleasesError();
|
throw NoReleasesError(note: tr('gitlabSourceNote'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return apkDetailsList.first;
|
return apkDetailsList.first;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:obtainium/components/generated_form.dart';
|
import 'package:obtainium/components/generated_form.dart';
|
||||||
import 'package:obtainium/custom_errors.dart';
|
import 'package:obtainium/custom_errors.dart';
|
||||||
|
import 'package:obtainium/providers/apps_provider.dart';
|
||||||
import 'package:obtainium/providers/source_provider.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
|
|
||||||
String ensureAbsoluteUrl(String ambiguousUrl, Uri referenceAbsoluteUrl) {
|
String ensureAbsoluteUrl(String ambiguousUrl, Uri referenceAbsoluteUrl) {
|
||||||
@@ -17,13 +17,19 @@ String ensureAbsoluteUrl(String ambiguousUrl, Uri referenceAbsoluteUrl) {
|
|||||||
.split('/')
|
.split('/')
|
||||||
.where((element) => element.trim().isNotEmpty)
|
.where((element) => element.trim().isNotEmpty)
|
||||||
.toList();
|
.toList();
|
||||||
|
String absoluteUrl;
|
||||||
if (ambiguousUrl.startsWith('/') || currPathSegments.isEmpty) {
|
if (ambiguousUrl.startsWith('/') || currPathSegments.isEmpty) {
|
||||||
return '${referenceAbsoluteUrl.origin}/$ambiguousUrl';
|
absoluteUrl = '${referenceAbsoluteUrl.origin}$ambiguousUrl';
|
||||||
|
} else if (currPathSegments.isEmpty) {
|
||||||
|
absoluteUrl = '${referenceAbsoluteUrl.origin}/$ambiguousUrl';
|
||||||
} else if (ambiguousUrl.split('/').where((e) => e.isNotEmpty).length == 1) {
|
} else if (ambiguousUrl.split('/').where((e) => e.isNotEmpty).length == 1) {
|
||||||
return '${referenceAbsoluteUrl.origin}/${currPathSegments.join('/')}/$ambiguousUrl';
|
absoluteUrl =
|
||||||
|
'${referenceAbsoluteUrl.origin}/${currPathSegments.join('/')}/$ambiguousUrl';
|
||||||
} else {
|
} else {
|
||||||
return '${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - 1).join('/')}/$ambiguousUrl';
|
absoluteUrl =
|
||||||
|
'${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - (currPathSegments.last.contains('.') ? 1 : 0)).join('/')}/$ambiguousUrl';
|
||||||
}
|
}
|
||||||
|
return Uri.parse(absoluteUrl).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
int compareAlphaNumeric(String a, String b) {
|
int compareAlphaNumeric(String a, String b) {
|
||||||
@@ -87,64 +93,116 @@ bool _isNumeric(String s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class HTML extends AppSource {
|
class HTML extends AppSource {
|
||||||
|
var finalStepFormitems = [
|
||||||
|
[
|
||||||
|
GeneratedFormTextField('customLinkFilterRegex',
|
||||||
|
label: tr('customLinkFilterRegex'),
|
||||||
|
hint: 'download/(.*/)?(android|apk|mobile)',
|
||||||
|
required: false,
|
||||||
|
additionalValidators: [
|
||||||
|
(value) {
|
||||||
|
return regExValidator(value);
|
||||||
|
}
|
||||||
|
])
|
||||||
|
],
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch('versionExtractWholePage',
|
||||||
|
label: tr('versionExtractWholePage'))
|
||||||
|
]
|
||||||
|
];
|
||||||
|
var commonFormItems = [
|
||||||
|
[GeneratedFormSwitch('filterByLinkText', label: tr('filterByLinkText'))],
|
||||||
|
[GeneratedFormSwitch('skipSort', label: tr('skipSort'))],
|
||||||
|
[GeneratedFormSwitch('reverseSort', label: tr('takeFirstLink'))],
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch('sortByLastLinkSegment',
|
||||||
|
label: tr('sortByLastLinkSegment'))
|
||||||
|
],
|
||||||
|
];
|
||||||
|
var intermediateFormItems = [
|
||||||
|
[
|
||||||
|
GeneratedFormTextField('customLinkFilterRegex',
|
||||||
|
label: tr('intermediateLinkRegex'),
|
||||||
|
hint: '([0-9]+.)*[0-9]+/\$',
|
||||||
|
required: true,
|
||||||
|
additionalValidators: [(value) => regExValidator(value)])
|
||||||
|
],
|
||||||
|
];
|
||||||
HTML() {
|
HTML() {
|
||||||
additionalSourceAppSpecificSettingFormItems = [
|
additionalSourceAppSpecificSettingFormItems = [
|
||||||
[
|
[
|
||||||
GeneratedFormSwitch('sortByFileNamesNotLinks',
|
GeneratedFormSubForm(
|
||||||
label: tr('sortByFileNamesNotLinks'))
|
'intermediateLink', [...intermediateFormItems, ...commonFormItems],
|
||||||
|
label: tr('intermediateLink'))
|
||||||
],
|
],
|
||||||
[GeneratedFormSwitch('reverseSort', label: tr('reverseSort'))],
|
finalStepFormitems[0],
|
||||||
|
...commonFormItems,
|
||||||
|
...finalStepFormitems.sublist(1),
|
||||||
[
|
[
|
||||||
GeneratedFormTextField('customLinkFilterRegex',
|
GeneratedFormSubForm(
|
||||||
label: tr('customLinkFilterRegex'),
|
'requestHeader',
|
||||||
hint: 'download/(.*/)?(android|apk|mobile)',
|
[
|
||||||
required: false,
|
[
|
||||||
additionalValidators: [
|
GeneratedFormTextField('requestHeader',
|
||||||
(value) {
|
label: tr('requestHeader'),
|
||||||
return regExValidator(value);
|
required: false,
|
||||||
|
additionalValidators: [
|
||||||
|
(value) {
|
||||||
|
if ((value ?? 'empty:valid')
|
||||||
|
.split(':')
|
||||||
|
.map((e) => e.trim())
|
||||||
|
.where((e) => e.isNotEmpty)
|
||||||
|
.length <
|
||||||
|
2) {
|
||||||
|
return tr('invalidInput');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
])
|
||||||
|
]
|
||||||
|
],
|
||||||
|
label: tr('requestHeader'),
|
||||||
|
defaultValue: [
|
||||||
|
{
|
||||||
|
'requestHeader':
|
||||||
|
'User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
GeneratedFormTextField('intermediateLinkRegex',
|
GeneratedFormDropdown(
|
||||||
label: tr('intermediateLinkRegex'),
|
'defaultPseudoVersioningMethod',
|
||||||
hint: '([0-9]+.)*[0-9]+/\$',
|
[
|
||||||
required: false,
|
MapEntry('partialAPKHash', tr('partialAPKHash')),
|
||||||
additionalValidators: [(value) => regExValidator(value)])
|
MapEntry('APKLinkHash', tr('APKLinkHash'))
|
||||||
],
|
],
|
||||||
[
|
label: tr('defaultPseudoVersioningMethod'),
|
||||||
GeneratedFormTextField('versionExtractionRegEx',
|
defaultValue: 'partialAPKHash')
|
||||||
label: tr('versionExtractionRegEx'),
|
|
||||||
required: false,
|
|
||||||
additionalValidators: [(value) => regExValidator(value)]),
|
|
||||||
GeneratedFormTextField('matchGroupToUse',
|
|
||||||
label: tr('matchGroupToUse'),
|
|
||||||
required: false,
|
|
||||||
hint: '1',
|
|
||||||
textInputType: const TextInputType.numberWithOptions(),
|
|
||||||
additionalValidators: [
|
|
||||||
(value) {
|
|
||||||
if (value?.isEmpty == true) {
|
|
||||||
value = null;
|
|
||||||
}
|
|
||||||
value ??= '1';
|
|
||||||
return intValidator(value);
|
|
||||||
}
|
|
||||||
])
|
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
overrideVersionDetectionFormDefault('noVersionDetection',
|
|
||||||
disableStandard: true, disableRelDate: true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Map<String, String>?> getRequestHeaders(
|
Future<Map<String, String>?> getRequestHeaders(
|
||||||
{Map<String, dynamic> additionalSettings = const <String, dynamic>{},
|
Map<String, dynamic> additionalSettings,
|
||||||
bool forAPKDownload = false}) async {
|
{bool forAPKDownload = false}) async {
|
||||||
return {
|
if (additionalSettings.isNotEmpty) {
|
||||||
"User-Agent":
|
if (additionalSettings['requestHeader']?.isNotEmpty != true) {
|
||||||
"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36"
|
additionalSettings['requestHeader'] = [];
|
||||||
};
|
}
|
||||||
|
additionalSettings['requestHeader'] = additionalSettings['requestHeader']
|
||||||
|
.where((l) => l['requestHeader'].isNotEmpty == true)
|
||||||
|
.toList();
|
||||||
|
Map<String, String> requestHeaders = {};
|
||||||
|
for (int i = 0; i < (additionalSettings['requestHeader'].length); i++) {
|
||||||
|
var temp =
|
||||||
|
(additionalSettings['requestHeader'][i]['requestHeader'] as String)
|
||||||
|
.split(':');
|
||||||
|
requestHeaders[temp[0].trim()] = temp.sublist(1).join(':').trim();
|
||||||
|
}
|
||||||
|
return requestHeaders;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -152,82 +210,117 @@ class HTML extends AppSource {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Given an HTTP response, grab some links according to the common additional settings
|
||||||
|
// (those that apply to intermediate and final steps)
|
||||||
|
Future<List<MapEntry<String, String>>> grabLinksCommon(
|
||||||
|
Response res, Map<String, dynamic> additionalSettings) async {
|
||||||
|
if (res.statusCode != 200) {
|
||||||
|
throw getObtainiumHttpError(res);
|
||||||
|
}
|
||||||
|
var html = parse(res.body);
|
||||||
|
List<MapEntry<String, String>> allLinks = html
|
||||||
|
.querySelectorAll('a')
|
||||||
|
.map((element) => MapEntry(
|
||||||
|
element.attributes['href'] ?? '',
|
||||||
|
element.text.isNotEmpty
|
||||||
|
? element.text
|
||||||
|
: (element.attributes['href'] ?? '').split('/').last))
|
||||||
|
.where((element) => element.key.isNotEmpty)
|
||||||
|
.map((e) =>
|
||||||
|
MapEntry(ensureAbsoluteUrl(e.key, res.request!.url), e.value))
|
||||||
|
.toList();
|
||||||
|
if (allLinks.isEmpty) {
|
||||||
|
allLinks = RegExp(
|
||||||
|
r'(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?')
|
||||||
|
.allMatches(res.body)
|
||||||
|
.map((match) =>
|
||||||
|
MapEntry(match.group(0)!, match.group(0)?.split('/').last ?? ''))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
List<MapEntry<String, String>> links = [];
|
||||||
|
bool skipSort = additionalSettings['skipSort'] == true;
|
||||||
|
bool filterLinkByText = additionalSettings['filterByLinkText'] == true;
|
||||||
|
if ((additionalSettings['customLinkFilterRegex'] as String?)?.isNotEmpty ==
|
||||||
|
true) {
|
||||||
|
var reg = RegExp(additionalSettings['customLinkFilterRegex']);
|
||||||
|
links = allLinks
|
||||||
|
.where((element) =>
|
||||||
|
reg.hasMatch(filterLinkByText ? element.value : element.key))
|
||||||
|
.toList();
|
||||||
|
} else {
|
||||||
|
links = allLinks
|
||||||
|
.where((element) =>
|
||||||
|
Uri.parse(filterLinkByText ? element.value : element.key)
|
||||||
|
.path
|
||||||
|
.toLowerCase()
|
||||||
|
.endsWith('.apk'))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
if (!skipSort) {
|
||||||
|
links.sort((a, b) => additionalSettings['sortByLastLinkSegment'] == true
|
||||||
|
? compareAlphaNumeric(
|
||||||
|
a.key.split('/').where((e) => e.isNotEmpty).last,
|
||||||
|
b.key.split('/').where((e) => e.isNotEmpty).last)
|
||||||
|
: compareAlphaNumeric(a.key, b.key));
|
||||||
|
}
|
||||||
|
if (additionalSettings['reverseSort'] == true) {
|
||||||
|
links = links.reversed.toList();
|
||||||
|
}
|
||||||
|
return links;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<APKDetails> getLatestAPKDetails(
|
Future<APKDetails> getLatestAPKDetails(
|
||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
var uri = Uri.parse(standardUrl);
|
var currentUrl = standardUrl;
|
||||||
Response res = await sourceRequest(standardUrl);
|
if (additionalSettings['intermediateLink']?.isNotEmpty != true) {
|
||||||
if (res.statusCode == 200) {
|
additionalSettings['intermediateLink'] = [];
|
||||||
var html = parse(res.body);
|
}
|
||||||
List<String> allLinks = html
|
additionalSettings['intermediateLink'] =
|
||||||
.querySelectorAll('a')
|
additionalSettings['intermediateLink']
|
||||||
.map((element) => element.attributes['href'] ?? '')
|
.where((l) => l['customLinkFilterRegex'].isNotEmpty == true)
|
||||||
.toList();
|
|
||||||
List<String> links = [];
|
|
||||||
if ((additionalSettings['intermediateLinkRegex'] as String?)
|
|
||||||
?.isNotEmpty ==
|
|
||||||
true) {
|
|
||||||
var reg = RegExp(additionalSettings['intermediateLinkRegex']);
|
|
||||||
links = allLinks.where((element) => reg.hasMatch(element)).toList();
|
|
||||||
links.sort((a, b) => compareAlphaNumeric(a, b));
|
|
||||||
if (links.isEmpty) {
|
|
||||||
throw ObtainiumError(tr('intermediateLinkNotFound'));
|
|
||||||
}
|
|
||||||
Map<String, dynamic> additionalSettingsTemp =
|
|
||||||
Map.from(additionalSettings);
|
|
||||||
additionalSettingsTemp['intermediateLinkRegex'] = null;
|
|
||||||
return getLatestAPKDetails(
|
|
||||||
ensureAbsoluteUrl(links.last, uri), additionalSettingsTemp);
|
|
||||||
}
|
|
||||||
if ((additionalSettings['customLinkFilterRegex'] as String?)
|
|
||||||
?.isNotEmpty ==
|
|
||||||
true) {
|
|
||||||
var reg = RegExp(additionalSettings['customLinkFilterRegex']);
|
|
||||||
links = allLinks.where((element) => reg.hasMatch(element)).toList();
|
|
||||||
} else {
|
|
||||||
links = allLinks
|
|
||||||
.where((element) =>
|
|
||||||
Uri.parse(element).path.toLowerCase().endsWith('.apk'))
|
|
||||||
.toList();
|
.toList();
|
||||||
|
for (int i = 0; i < (additionalSettings['intermediateLink'].length); i++) {
|
||||||
|
var intLinks = await grabLinksCommon(
|
||||||
|
await sourceRequest(currentUrl, additionalSettings),
|
||||||
|
additionalSettings['intermediateLink'][i]);
|
||||||
|
if (intLinks.isEmpty) {
|
||||||
|
throw NoReleasesError();
|
||||||
|
} else {
|
||||||
|
currentUrl = intLinks.last.key;
|
||||||
}
|
}
|
||||||
links.sort((a, b) => additionalSettings['sortByFileNamesNotLinks'] == true
|
}
|
||||||
? compareAlphaNumeric(a.split('/').where((e) => e.isNotEmpty).last,
|
var uri = Uri.parse(currentUrl);
|
||||||
b.split('/').where((e) => e.isNotEmpty).last)
|
List<MapEntry<String, String>> links = [];
|
||||||
: compareAlphaNumeric(a, b));
|
String versionExtractionWholePageString = currentUrl;
|
||||||
if (additionalSettings['reverseSort'] == true) {
|
if (additionalSettings['directAPKLink'] != true) {
|
||||||
links = links.reversed.toList();
|
Response res = await sourceRequest(currentUrl, additionalSettings);
|
||||||
}
|
versionExtractionWholePageString =
|
||||||
if ((additionalSettings['apkFilterRegEx'] as String?)?.isNotEmpty ==
|
res.body.split('\r\n').join('\n').split('\n').join('\\n');
|
||||||
true) {
|
links = await grabLinksCommon(res, additionalSettings);
|
||||||
var reg = RegExp(additionalSettings['apkFilterRegEx']);
|
links = filterApks(links, additionalSettings['apkFilterRegEx'],
|
||||||
links = links.where((element) => reg.hasMatch(element)).toList();
|
additionalSettings['invertAPKFilter']);
|
||||||
}
|
|
||||||
if (links.isEmpty) {
|
if (links.isEmpty) {
|
||||||
throw NoReleasesError();
|
throw NoReleasesError();
|
||||||
}
|
}
|
||||||
var rel = links.last;
|
|
||||||
String? version = rel.hashCode.toString();
|
|
||||||
var versionExtractionRegEx =
|
|
||||||
additionalSettings['versionExtractionRegEx'] as String?;
|
|
||||||
if (versionExtractionRegEx?.isNotEmpty == true) {
|
|
||||||
var match = RegExp(versionExtractionRegEx!).allMatches(rel);
|
|
||||||
if (match.isEmpty) {
|
|
||||||
throw NoVersionError();
|
|
||||||
}
|
|
||||||
version = match.last
|
|
||||||
.group(int.parse(additionalSettings['matchGroupToUse'] as String));
|
|
||||||
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(),
|
|
||||||
AppNames(uri.host, tr('app')));
|
|
||||||
} else {
|
} else {
|
||||||
throw getObtainiumHttpError(res);
|
links = [MapEntry(currentUrl, currentUrl)];
|
||||||
}
|
}
|
||||||
|
var rel = links.last.key;
|
||||||
|
String? version;
|
||||||
|
version = extractVersion(
|
||||||
|
additionalSettings['versionExtractionRegEx'] as String?,
|
||||||
|
additionalSettings['matchGroupToUse'] as String?,
|
||||||
|
additionalSettings['versionExtractWholePage'] == true
|
||||||
|
? versionExtractionWholePageString
|
||||||
|
: rel);
|
||||||
|
version ??=
|
||||||
|
additionalSettings['defaultPseudoVersioningMethod'] == 'APKLinkHash'
|
||||||
|
? rel.hashCode.toString()
|
||||||
|
: (await checkPartialDownloadHashDynamc(rel)).toString();
|
||||||
|
return APKDetails(version, [rel].map((e) => MapEntry(e, e)).toList(),
|
||||||
|
AppNames(uri.host, tr('app')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,26 +6,30 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
class HuaweiAppGallery extends AppSource {
|
class HuaweiAppGallery extends AppSource {
|
||||||
HuaweiAppGallery() {
|
HuaweiAppGallery() {
|
||||||
name = 'Huawei AppGallery';
|
name = 'Huawei AppGallery';
|
||||||
host = 'appgallery.huawei.com';
|
hosts = ['appgallery.huawei.com'];
|
||||||
overrideVersionDetectionFormDefault('releaseDateAsVersion',
|
versionDetectionDisallowed = true;
|
||||||
disableStandard: true);
|
showReleaseDateAsVersionToggle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/app/[^/]+');
|
RegExp standardUrlRegEx = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/app/[^/]+',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDlUrl(String standardUrl) =>
|
getDlUrl(String standardUrl) =>
|
||||||
'https://${host!.replaceAll('appgallery.', 'appgallery.cloud.')}/appdl/${standardUrl.split('/').last}';
|
'https://${hosts[0].replaceAll('appgallery.', 'appgallery.cloud.')}/appdl/${standardUrl.split('/').last}';
|
||||||
|
|
||||||
requestAppdlRedirect(String dlUrl) async {
|
requestAppdlRedirect(
|
||||||
Response res = await sourceRequest(dlUrl, followRedirects: false);
|
String dlUrl, Map<String, dynamic> additionalSettings) async {
|
||||||
|
Response res =
|
||||||
|
await sourceRequest(dlUrl, additionalSettings, followRedirects: false);
|
||||||
if (res.statusCode == 200 ||
|
if (res.statusCode == 200 ||
|
||||||
res.statusCode == 302 ||
|
res.statusCode == 302 ||
|
||||||
res.statusCode == 304) {
|
res.statusCode == 304) {
|
||||||
@@ -52,7 +56,7 @@ class HuaweiAppGallery extends AppSource {
|
|||||||
Future<String?> tryInferringAppId(String standardUrl,
|
Future<String?> tryInferringAppId(String standardUrl,
|
||||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||||
String dlUrl = getDlUrl(standardUrl);
|
String dlUrl = getDlUrl(standardUrl);
|
||||||
Response res = await requestAppdlRedirect(dlUrl);
|
Response res = await requestAppdlRedirect(dlUrl, additionalSettings);
|
||||||
return res.headers['location'] != null
|
return res.headers['location'] != null
|
||||||
? appIdFromRedirectDlUrl(res.headers['location']!)
|
? appIdFromRedirectDlUrl(res.headers['location']!)
|
||||||
: null;
|
: null;
|
||||||
@@ -64,7 +68,7 @@ class HuaweiAppGallery extends AppSource {
|
|||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
String dlUrl = getDlUrl(standardUrl);
|
String dlUrl = getDlUrl(standardUrl);
|
||||||
Response res = await requestAppdlRedirect(dlUrl);
|
Response res = await requestAppdlRedirect(dlUrl, additionalSettings);
|
||||||
if (res.headers['location'] == null) {
|
if (res.headers['location'] == null) {
|
||||||
throw NoReleasesError();
|
throw NoReleasesError();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class IzzyOnDroid extends AppSource {
|
|||||||
late FDroid fd;
|
late FDroid fd;
|
||||||
|
|
||||||
IzzyOnDroid() {
|
IzzyOnDroid() {
|
||||||
host = 'izzysoft.de';
|
hosts = ['izzysoft.de'];
|
||||||
fd = FDroid();
|
fd = FDroid();
|
||||||
additionalSourceAppSpecificSettingFormItems =
|
additionalSourceAppSpecificSettingFormItems =
|
||||||
fd.additionalSourceAppSpecificSettingFormItems;
|
fd.additionalSourceAppSpecificSettingFormItems;
|
||||||
@@ -15,17 +15,20 @@ class IzzyOnDroid extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegExA = RegExp('^https?://android.$host/repo/apk/[^/]+');
|
RegExp standardUrlRegExA = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
'^https?://android.${getSourceRegex(hosts)}/repo/apk/[^/]+',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegExA.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
RegExp standardUrlRegExB =
|
RegExp standardUrlRegExB = RegExp(
|
||||||
RegExp('^https?://apt.$host/fdroid/index/apk/[^/]+');
|
'^https?://apt.${getSourceRegex(hosts)}/fdroid/index/apk/[^/]+',
|
||||||
match = standardUrlRegExB.firstMatch(url.toLowerCase());
|
caseSensitive: false);
|
||||||
|
match = standardUrlRegExB.firstMatch(url);
|
||||||
}
|
}
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -40,9 +43,10 @@ class IzzyOnDroid extends AppSource {
|
|||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
String? appId = await tryInferringAppId(standardUrl);
|
String? appId = await tryInferringAppId(standardUrl);
|
||||||
return getAPKUrlsFromFDroidPackagesAPIResponse(
|
return fd.getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||||
await sourceRequest(
|
await sourceRequest(
|
||||||
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),
|
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId',
|
||||||
|
additionalSettings),
|
||||||
'https://android.izzysoft.de/frepo/$appId',
|
'https://android.izzysoft.de/frepo/$appId',
|
||||||
standardUrl,
|
standardUrl,
|
||||||
name,
|
name,
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class Jenkins extends AppSource {
|
class Jenkins extends AppSource {
|
||||||
Jenkins() {
|
Jenkins() {
|
||||||
overrideVersionDetectionFormDefault('releaseDateAsVersion',
|
versionDetectionDisallowed = true;
|
||||||
disableStandard: true);
|
neverAutoSelect = true;
|
||||||
|
showReleaseDateAsVersionToggle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String trimJobUrl(String url) {
|
String trimJobUrl(String url) {
|
||||||
@@ -16,7 +17,7 @@ class Jenkins extends AppSource {
|
|||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -29,8 +30,8 @@ class Jenkins extends AppSource {
|
|||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
standardUrl = trimJobUrl(standardUrl);
|
standardUrl = trimJobUrl(standardUrl);
|
||||||
Response res =
|
Response res = await sourceRequest(
|
||||||
await sourceRequest('$standardUrl/lastSuccessfulBuild/api/json');
|
'$standardUrl/lastSuccessfulBuild/api/json', additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var json = jsonDecode(res.body);
|
var json = jsonDecode(res.body);
|
||||||
var releaseDate = json['timestamp'] == null
|
var releaseDate = json['timestamp'] == null
|
||||||
|
|||||||
@@ -6,17 +6,19 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class Mullvad extends AppSource {
|
class Mullvad extends AppSource {
|
||||||
Mullvad() {
|
Mullvad() {
|
||||||
host = 'mullvad.net';
|
hosts = ['mullvad.net'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host');
|
RegExp standardUrlRegEx = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -28,7 +30,8 @@ class Mullvad extends AppSource {
|
|||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
Response res = await sourceRequest('$standardUrl/en/download/android');
|
Response res = await sourceRequest(
|
||||||
|
'$standardUrl/en/download/android', additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var versions = parse(res.body)
|
var versions = parse(res.body)
|
||||||
.querySelectorAll('p')
|
.querySelectorAll('p')
|
||||||
|
|||||||
@@ -5,17 +5,20 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class NeutronCode extends AppSource {
|
class NeutronCode extends AppSource {
|
||||||
NeutronCode() {
|
NeutronCode() {
|
||||||
host = 'neutroncode.com';
|
hosts = ['neutroncode.com'];
|
||||||
|
showReleaseDateAsVersionToggle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/downloads/file/[^/]+');
|
RegExp standardUrlRegEx = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/downloads/file/[^/]+',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -78,7 +81,7 @@ class NeutronCode extends AppSource {
|
|||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
Response res = await sourceRequest(standardUrl);
|
Response res = await sourceRequest(standardUrl, additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var http = parse(res.body);
|
var http = parse(res.body);
|
||||||
var name = http.querySelector('.pd-title')?.innerHtml;
|
var name = http.querySelector('.pd-title')?.innerHtml;
|
||||||
@@ -91,7 +94,7 @@ class NeutronCode extends AppSource {
|
|||||||
if (version == null) {
|
if (version == null) {
|
||||||
throw NoVersionError();
|
throw NoVersionError();
|
||||||
}
|
}
|
||||||
String? apkUrl = 'https://$host/download/$filename';
|
String? apkUrl = 'https://${hosts[0]}/download/$filename';
|
||||||
var dateStringOriginal =
|
var dateStringOriginal =
|
||||||
http.querySelector('.pd-date-txt')?.nextElementSibling?.innerHtml;
|
http.querySelector('.pd-date-txt')?.nextElementSibling?.innerHtml;
|
||||||
var dateString = dateStringOriginal != null
|
var dateString = dateStringOriginal != null
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class Signal extends AppSource {
|
class Signal extends AppSource {
|
||||||
Signal() {
|
Signal() {
|
||||||
host = 'signal.org';
|
hosts = ['signal.org'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
return 'https://$host';
|
return 'https://${hosts[0]}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -18,8 +18,8 @@ class Signal extends AppSource {
|
|||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
Response res =
|
Response res = await sourceRequest(
|
||||||
await sourceRequest('https://updates.$host/android/latest.json');
|
'https://updates.${hosts[0]}/android/latest.json', additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var json = jsonDecode(res.body);
|
var json = jsonDecode(res.body);
|
||||||
String? apkUrl = json['url'];
|
String? apkUrl = json['url'];
|
||||||
|
|||||||
@@ -5,23 +5,27 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class SourceForge extends AppSource {
|
class SourceForge extends AppSource {
|
||||||
SourceForge() {
|
SourceForge() {
|
||||||
host = 'sourceforge.net';
|
hosts = ['sourceforge.net'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegExB = RegExp('^https?://$host/p/[^/]+');
|
RegExp standardUrlRegExB = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/p/[^/]+',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegExB.firstMatch(url);
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
url =
|
url =
|
||||||
'https://${Uri.parse(url.substring(0, match.end)).host}/projects/${url.substring(Uri.parse(url.substring(0, match.end)).host.length + '/projects/'.length + 1)}';
|
'https://${Uri.parse(match.group(0)!).host}/projects/${url.substring(Uri.parse(match.group(0)!).host.length + '/projects/'.length + 1)}';
|
||||||
}
|
}
|
||||||
RegExp standardUrlRegExA = RegExp('^https?://$host/projects/[^/]+');
|
RegExp standardUrlRegExA = RegExp(
|
||||||
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/projects/[^/]+',
|
||||||
|
caseSensitive: false);
|
||||||
|
match = standardUrlRegExA.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -29,7 +33,8 @@ class SourceForge extends AppSource {
|
|||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
Response res = await sourceRequest('$standardUrl/rss?path=/');
|
Response res =
|
||||||
|
await sourceRequest('$standardUrl/rss?path=/', additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var parsedHtml = parse(res.body);
|
var parsedHtml = parse(res.body);
|
||||||
var allDownloadLinks =
|
var allDownloadLinks =
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
|
|
||||||
class SourceHut extends AppSource {
|
class SourceHut extends AppSource {
|
||||||
SourceHut() {
|
SourceHut() {
|
||||||
host = 'git.sr.ht';
|
hosts = ['git.sr.ht'];
|
||||||
|
showReleaseDateAsVersionToggle = true;
|
||||||
|
|
||||||
additionalSourceAppSpecificSettingFormItems = [
|
additionalSourceAppSpecificSettingFormItems = [
|
||||||
[
|
[
|
||||||
@@ -20,12 +21,14 @@ class SourceHut extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
'^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return url.substring(0, match.end);
|
return match.group(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -36,11 +39,21 @@ class SourceHut extends AppSource {
|
|||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
|
if (standardUrl.endsWith('/refs')) {
|
||||||
|
standardUrl = standardUrl
|
||||||
|
.split('/')
|
||||||
|
.reversed
|
||||||
|
.toList()
|
||||||
|
.sublist(1)
|
||||||
|
.reversed
|
||||||
|
.join('/');
|
||||||
|
}
|
||||||
Uri standardUri = Uri.parse(standardUrl);
|
Uri standardUri = Uri.parse(standardUrl);
|
||||||
String appName = standardUri.pathSegments.last;
|
String appName = standardUri.pathSegments.last;
|
||||||
bool fallbackToOlderReleases =
|
bool fallbackToOlderReleases =
|
||||||
additionalSettings['fallbackToOlderReleases'] == true;
|
additionalSettings['fallbackToOlderReleases'] == true;
|
||||||
Response res = await sourceRequest('$standardUrl/refs/rss.xml');
|
Response res =
|
||||||
|
await sourceRequest('$standardUrl/refs/rss.xml', additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var parsedHtml = parse(res.body);
|
var parsedHtml = parse(res.body);
|
||||||
List<APKDetails> apkDetailsList = [];
|
List<APKDetails> apkDetailsList = [];
|
||||||
@@ -69,7 +82,7 @@ class SourceHut extends AppSource {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
var res2 = await sourceRequest(releasePage);
|
var res2 = await sourceRequest(releasePage, additionalSettings);
|
||||||
List<MapEntry<String, String>> apkUrls = [];
|
List<MapEntry<String, String>> apkUrls = [];
|
||||||
if (res2.statusCode == 200) {
|
if (res2.statusCode == 200) {
|
||||||
apkUrls = getApkUrlsFromUrls(parse(res2.body)
|
apkUrls = getApkUrlsFromUrls(parse(res2.body)
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class SteamMobile extends AppSource {
|
class SteamMobile extends AppSource {
|
||||||
SteamMobile() {
|
SteamMobile() {
|
||||||
host = 'store.steampowered.com';
|
hosts = ['store.steampowered.com'];
|
||||||
name = tr('steam');
|
name = 'Steam';
|
||||||
additionalSourceAppSpecificSettingFormItems = [
|
additionalSourceAppSpecificSettingFormItems = [
|
||||||
[
|
[
|
||||||
GeneratedFormDropdown('app', apks.entries.toList(),
|
GeneratedFormDropdown('app', apks.entries.toList(),
|
||||||
@@ -21,7 +21,7 @@ class SteamMobile extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
return 'https://$host';
|
return 'https://${hosts[0]}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -29,7 +29,8 @@ class SteamMobile extends AppSource {
|
|||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
Response res = await sourceRequest('https://$host/mobile');
|
Response res =
|
||||||
|
await sourceRequest('https://${hosts[0]}/mobile', additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var apkNamePrefix = additionalSettings['app'] as String?;
|
var apkNamePrefix = additionalSettings['app'] as String?;
|
||||||
if (apkNamePrefix == null) {
|
if (apkNamePrefix == null) {
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class TelegramApp extends AppSource {
|
class TelegramApp extends AppSource {
|
||||||
TelegramApp() {
|
TelegramApp() {
|
||||||
host = 'telegram.org';
|
hosts = ['telegram.org'];
|
||||||
name = 'Telegram ${tr('app')}';
|
name = 'Telegram ${tr('app')}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
return 'https://$host';
|
return 'https://${hosts[0]}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -20,7 +20,8 @@ class TelegramApp extends AppSource {
|
|||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
Response res = await sourceRequest('https://t.me/s/TAndroidAPK');
|
Response res =
|
||||||
|
await sourceRequest('https://t.me/s/TAndroidAPK', additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var http = parse(res.body);
|
var http = parse(res.body);
|
||||||
var messages =
|
var messages =
|
||||||
|
|||||||
@@ -6,35 +6,41 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class Uptodown extends AppSource {
|
class Uptodown extends AppSource {
|
||||||
Uptodown() {
|
Uptodown() {
|
||||||
host = 'uptodown.com';
|
hosts = ['uptodown.com'];
|
||||||
allowSubDomains = true;
|
allowSubDomains = true;
|
||||||
|
naiveStandardVersionDetection = true;
|
||||||
|
showReleaseDateAsVersionToggle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://([^\\.]+\\.){2,}$host');
|
RegExp standardUrlRegEx = RegExp(
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
'^https?://([^\\.]+\\.){2,}${getSourceRegex(hosts)}',
|
||||||
|
caseSensitive: false);
|
||||||
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
}
|
}
|
||||||
return '${url.substring(0, match.end)}/android/download';
|
return '${match.group(0)!}/android/download';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String?> tryInferringAppId(String standardUrl,
|
Future<String?> tryInferringAppId(String standardUrl,
|
||||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||||
return (await getAppDetailsFromPage(standardUrl))['appId'];
|
return (await getAppDetailsFromPage(
|
||||||
|
standardUrl, additionalSettings))['appId'];
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, String?>> getAppDetailsFromPage(String standardUrl) async {
|
Future<Map<String, String?>> getAppDetailsFromPage(
|
||||||
var res = await sourceRequest(standardUrl);
|
String standardUrl, Map<String, dynamic> additionalSettings) async {
|
||||||
|
var res = await sourceRequest(standardUrl, additionalSettings);
|
||||||
if (res.statusCode != 200) {
|
if (res.statusCode != 200) {
|
||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
}
|
}
|
||||||
var html = parse(res.body);
|
var html = parse(res.body);
|
||||||
String? version = html.querySelector('div.version')?.innerHtml;
|
String? version = html.querySelector('div.version')?.innerHtml;
|
||||||
String? apkUrl =
|
String? apkUrl =
|
||||||
html.querySelector('#detail-download-button')?.attributes['data-url'];
|
'${standardUrl.split('/').reversed.toList().sublist(1).reversed.join('/')}/post-download';
|
||||||
String? name = html.querySelector('#detail-app-name')?.innerHtml.trim();
|
String? name = html.querySelector('#detail-app-name')?.innerHtml.trim();
|
||||||
String? author = html.querySelector('#author-link')?.innerHtml.trim();
|
String? author = html.querySelector('#author-link')?.innerHtml.trim();
|
||||||
var detailElements = html.querySelectorAll('#technical-information td');
|
var detailElements = html.querySelectorAll('#technical-information td');
|
||||||
@@ -55,7 +61,8 @@ class Uptodown extends AppSource {
|
|||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
var appDetails = await getAppDetailsFromPage(standardUrl);
|
var appDetails =
|
||||||
|
await getAppDetailsFromPage(standardUrl, additionalSettings);
|
||||||
var version = appDetails['version'];
|
var version = appDetails['version'];
|
||||||
var apkUrl = appDetails['apkUrl'];
|
var apkUrl = appDetails['apkUrl'];
|
||||||
var appId = appDetails['appId'];
|
var appId = appDetails['appId'];
|
||||||
@@ -79,4 +86,20 @@ class Uptodown extends AppSource {
|
|||||||
version, getApkUrlsFromUrls([apkUrl]), AppNames(author, appName),
|
version, getApkUrlsFromUrls([apkUrl]), AppNames(author, appName),
|
||||||
releaseDate: relDate);
|
releaseDate: relDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
|
||||||
|
Map<String, dynamic> additionalSettings) async {
|
||||||
|
var res = await sourceRequest(apkUrl, additionalSettings);
|
||||||
|
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.${hosts[0]}/dwn/$finalUrlKey';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,33 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:obtainium/app_sources/html.dart';
|
|
||||||
import 'package:obtainium/custom_errors.dart';
|
import 'package:obtainium/custom_errors.dart';
|
||||||
import 'package:obtainium/providers/source_provider.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
|
|
||||||
class VLC extends AppSource {
|
class VLC extends AppSource {
|
||||||
VLC() {
|
VLC() {
|
||||||
host = 'videolan.org';
|
hosts = ['videolan.org'];
|
||||||
}
|
}
|
||||||
get dwUrlBase => 'https://get.$host/vlc-android/';
|
get dwUrlBase => 'https://get.${hosts[0]}/vlc-android/';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Map<String, String>?> getRequestHeaders(
|
Future<Map<String, String>?> getRequestHeaders(
|
||||||
{Map<String, dynamic> additionalSettings = const <String, dynamic>{},
|
Map<String, dynamic> additionalSettings,
|
||||||
bool forAPKDownload = false}) =>
|
{bool forAPKDownload = false}) async {
|
||||||
HTML().getRequestHeaders(
|
return {
|
||||||
additionalSettings: additionalSettings,
|
"User-Agent":
|
||||||
forAPKDownload: forAPKDownload);
|
"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
return 'https://$host';
|
return 'https://${hosts[0]}';
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> getLatestVersion(String standardUrl) async {
|
Future<String?> getLatestVersion(
|
||||||
Response res = await sourceRequest(dwUrlBase);
|
String standardUrl, Map<String, dynamic> additionalSettings) async {
|
||||||
|
Response res = await sourceRequest(dwUrlBase, additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var dwLinks = parse(res.body)
|
var dwLinks = parse(res.body)
|
||||||
.querySelectorAll('a')
|
.querySelectorAll('a')
|
||||||
@@ -77,9 +79,9 @@ class VLC extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> apkUrlPrefetchModifier(
|
Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
|
||||||
String apkUrl, String standardUrl) async {
|
Map<String, dynamic> additionalSettings) async {
|
||||||
Response res = await sourceRequest(apkUrl);
|
Response res = await sourceRequest(apkUrl, additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
String? apkUrl =
|
String? apkUrl =
|
||||||
parse(res.body).querySelector('#alt_link')?.attributes['href'];
|
parse(res.body).querySelector('#alt_link')?.attributes['href'];
|
||||||
|
|||||||
@@ -5,25 +5,26 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class WhatsApp extends AppSource {
|
class WhatsApp extends AppSource {
|
||||||
WhatsApp() {
|
WhatsApp() {
|
||||||
host = 'whatsapp.com';
|
hosts = ['whatsapp.com'];
|
||||||
|
versionDetectionDisallowed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
return 'https://$host';
|
return 'https://${hosts[0]}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> apkUrlPrefetchModifier(
|
Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
|
||||||
String apkUrl, String standardUrl) async {
|
Map<String, dynamic> additionalSettings) async {
|
||||||
Response res = await sourceRequest('https://www.whatsapp.com/android');
|
Response res =
|
||||||
|
await sourceRequest('$standardUrl/android', additionalSettings);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var targetLinks = parse(res.body)
|
var targetLinks = parse(res.body)
|
||||||
.querySelectorAll('a')
|
.querySelectorAll('a')
|
||||||
.map((e) => e.attributes['href'] ?? '')
|
.map((e) => e.attributes['href'] ?? '')
|
||||||
.where((e) => e.isNotEmpty)
|
.where((e) => e.isNotEmpty)
|
||||||
.where((e) =>
|
.where((e) => e.contains('WhatsApp.apk'))
|
||||||
e.contains('content.whatsapp.net') && e.contains('WhatsApp.apk'))
|
|
||||||
.toList();
|
.toList();
|
||||||
if (targetLinks.isEmpty) {
|
if (targetLinks.isEmpty) {
|
||||||
throw NoAPKError();
|
throw NoAPKError();
|
||||||
@@ -39,37 +40,16 @@ class WhatsApp extends AppSource {
|
|||||||
String standardUrl,
|
String standardUrl,
|
||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
Response res = await sourceRequest('https://www.whatsapp.com/android');
|
// This is a CDN link that is consistent per version
|
||||||
if (res.statusCode == 200) {
|
// But it has query params that change constantly
|
||||||
var targetElements = parse(res.body)
|
Uri apkUri = Uri.parse(await apkUrlPrefetchModifier(
|
||||||
.querySelectorAll('p')
|
standardUrl, standardUrl, additionalSettings));
|
||||||
.where((element) => element.innerHtml.contains('Version '))
|
var unusableApkUrl = '${apkUri.origin}/${apkUri.path}';
|
||||||
.toList();
|
// So we use the param-less URL is a pseudo-version to add the app and check for updates
|
||||||
if (targetElements.isEmpty) {
|
// See #357 for why we can't scrape the version number directly
|
||||||
throw NoVersionError();
|
// But we re-fetch the URL again with its latest query params at the actual download time
|
||||||
}
|
String version = unusableApkUrl.hashCode.toString();
|
||||||
var vLines = targetElements[0]
|
return APKDetails(version, getApkUrlsFromUrls([unusableApkUrl]),
|
||||||
.innerHtml
|
AppNames('Meta', 'WhatsApp'));
|
||||||
.split('\n')
|
|
||||||
.where((element) => element.contains('Version '))
|
|
||||||
.toList();
|
|
||||||
if (vLines.isEmpty) {
|
|
||||||
throw NoVersionError();
|
|
||||||
}
|
|
||||||
var versionMatch = RegExp('[0-9]+(\\.[0-9]+)+').firstMatch(vLines[0]);
|
|
||||||
if (versionMatch == null) {
|
|
||||||
throw NoVersionError();
|
|
||||||
}
|
|
||||||
String version =
|
|
||||||
vLines[0].substring(versionMatch.start, versionMatch.end);
|
|
||||||
return APKDetails(
|
|
||||||
version,
|
|
||||||
getApkUrlsFromUrls([
|
|
||||||
'https://www.whatsapp.com/android?v=$version&=thisIsaPlaceholder&a=realURLPrefetchedAtDownloadTime'
|
|
||||||
]),
|
|
||||||
AppNames('Meta', 'WhatsApp'));
|
|
||||||
} else {
|
|
||||||
throw getObtainiumHttpError(res);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:hsluv/hsluv.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:obtainium/components/generated_form_modal.dart';
|
import 'package:obtainium/components/generated_form_modal.dart';
|
||||||
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
|
|
||||||
abstract class GeneratedFormItem {
|
abstract class GeneratedFormItem {
|
||||||
late String key;
|
late String key;
|
||||||
@@ -12,6 +13,7 @@ abstract class GeneratedFormItem {
|
|||||||
late dynamic defaultValue;
|
late dynamic defaultValue;
|
||||||
List<dynamic> additionalValidators;
|
List<dynamic> additionalValidators;
|
||||||
dynamic ensureType(dynamic val);
|
dynamic ensureType(dynamic val);
|
||||||
|
GeneratedFormItem clone();
|
||||||
|
|
||||||
GeneratedFormItem(this.key,
|
GeneratedFormItem(this.key,
|
||||||
{this.label = 'Input',
|
{this.label = 'Input',
|
||||||
@@ -27,26 +29,36 @@ class GeneratedFormTextField extends GeneratedFormItem {
|
|||||||
late bool password;
|
late bool password;
|
||||||
late TextInputType? textInputType;
|
late TextInputType? textInputType;
|
||||||
|
|
||||||
GeneratedFormTextField(String key,
|
GeneratedFormTextField(super.key,
|
||||||
{String label = 'Input',
|
{super.label,
|
||||||
List<Widget> belowWidgets = const [],
|
super.belowWidgets,
|
||||||
String defaultValue = '',
|
String super.defaultValue = '',
|
||||||
List<String? Function(String? value)> additionalValidators = const [],
|
List<String? Function(String? value)> super.additionalValidators =
|
||||||
|
const [],
|
||||||
this.required = true,
|
this.required = true,
|
||||||
this.max = 1,
|
this.max = 1,
|
||||||
this.hint,
|
this.hint,
|
||||||
this.password = false,
|
this.password = false,
|
||||||
this.textInputType})
|
this.textInputType});
|
||||||
: super(key,
|
|
||||||
label: label,
|
|
||||||
belowWidgets: belowWidgets,
|
|
||||||
defaultValue: defaultValue,
|
|
||||||
additionalValidators: additionalValidators);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String ensureType(val) {
|
String ensureType(val) {
|
||||||
return val.toString();
|
return val.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
GeneratedFormTextField clone() {
|
||||||
|
return GeneratedFormTextField(key,
|
||||||
|
label: label,
|
||||||
|
belowWidgets: belowWidgets,
|
||||||
|
defaultValue: defaultValue,
|
||||||
|
additionalValidators: List.from(additionalValidators),
|
||||||
|
required: required,
|
||||||
|
max: max,
|
||||||
|
hint: hint,
|
||||||
|
password: password,
|
||||||
|
textInputType: textInputType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GeneratedFormDropdown extends GeneratedFormItem {
|
class GeneratedFormDropdown extends GeneratedFormItem {
|
||||||
@@ -54,42 +66,61 @@ class GeneratedFormDropdown extends GeneratedFormItem {
|
|||||||
List<String>? disabledOptKeys;
|
List<String>? disabledOptKeys;
|
||||||
|
|
||||||
GeneratedFormDropdown(
|
GeneratedFormDropdown(
|
||||||
String key,
|
super.key,
|
||||||
this.opts, {
|
this.opts, {
|
||||||
String label = 'Input',
|
super.label,
|
||||||
List<Widget> belowWidgets = const [],
|
super.belowWidgets,
|
||||||
String defaultValue = '',
|
String super.defaultValue = '',
|
||||||
this.disabledOptKeys,
|
this.disabledOptKeys,
|
||||||
List<String? Function(String? value)> additionalValidators = const [],
|
List<String? Function(String? value)> super.additionalValidators = const [],
|
||||||
}) : super(key,
|
});
|
||||||
label: label,
|
|
||||||
belowWidgets: belowWidgets,
|
|
||||||
defaultValue: defaultValue,
|
|
||||||
additionalValidators: additionalValidators);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String ensureType(val) {
|
String ensureType(val) {
|
||||||
return val.toString();
|
return val.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
GeneratedFormDropdown clone() {
|
||||||
|
return GeneratedFormDropdown(
|
||||||
|
key,
|
||||||
|
opts?.map((e) => MapEntry(e.key, e.value)).toList(),
|
||||||
|
label: label,
|
||||||
|
belowWidgets: belowWidgets,
|
||||||
|
defaultValue: defaultValue,
|
||||||
|
disabledOptKeys:
|
||||||
|
disabledOptKeys != null ? List.from(disabledOptKeys!) : null,
|
||||||
|
additionalValidators: List.from(additionalValidators),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GeneratedFormSwitch extends GeneratedFormItem {
|
class GeneratedFormSwitch extends GeneratedFormItem {
|
||||||
|
bool disabled = false;
|
||||||
|
|
||||||
GeneratedFormSwitch(
|
GeneratedFormSwitch(
|
||||||
String key, {
|
super.key, {
|
||||||
String label = 'Input',
|
super.label,
|
||||||
List<Widget> belowWidgets = const [],
|
super.belowWidgets,
|
||||||
bool defaultValue = false,
|
bool super.defaultValue = false,
|
||||||
List<String? Function(bool value)> additionalValidators = const [],
|
bool disabled = false,
|
||||||
}) : super(key,
|
List<String? Function(bool value)> super.additionalValidators = const [],
|
||||||
label: label,
|
});
|
||||||
belowWidgets: belowWidgets,
|
|
||||||
defaultValue: defaultValue,
|
|
||||||
additionalValidators: additionalValidators);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool ensureType(val) {
|
bool ensureType(val) {
|
||||||
return val == true || val == 'true';
|
return val == true || val == 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
GeneratedFormSwitch clone() {
|
||||||
|
return GeneratedFormSwitch(key,
|
||||||
|
label: label,
|
||||||
|
belowWidgets: belowWidgets,
|
||||||
|
defaultValue: defaultValue,
|
||||||
|
disabled: false,
|
||||||
|
additionalValidators: List.from(additionalValidators));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GeneratedFormTagInput extends GeneratedFormItem {
|
class GeneratedFormTagInput extends GeneratedFormItem {
|
||||||
@@ -98,27 +129,36 @@ class GeneratedFormTagInput extends GeneratedFormItem {
|
|||||||
late WrapAlignment alignment;
|
late WrapAlignment alignment;
|
||||||
late String emptyMessage;
|
late String emptyMessage;
|
||||||
late bool showLabelWhenNotEmpty;
|
late bool showLabelWhenNotEmpty;
|
||||||
GeneratedFormTagInput(String key,
|
GeneratedFormTagInput(super.key,
|
||||||
{String label = 'Input',
|
{super.label,
|
||||||
List<Widget> belowWidgets = const [],
|
super.belowWidgets,
|
||||||
Map<String, MapEntry<int, bool>> defaultValue = const {},
|
Map<String, MapEntry<int, bool>> super.defaultValue = const {},
|
||||||
List<String? Function(Map<String, MapEntry<int, bool>> value)>
|
List<String? Function(Map<String, MapEntry<int, bool>> value)>
|
||||||
additionalValidators = const [],
|
super.additionalValidators = const [],
|
||||||
this.deleteConfirmationMessage,
|
this.deleteConfirmationMessage,
|
||||||
this.singleSelect = false,
|
this.singleSelect = false,
|
||||||
this.alignment = WrapAlignment.start,
|
this.alignment = WrapAlignment.start,
|
||||||
this.emptyMessage = 'Input',
|
this.emptyMessage = 'Input',
|
||||||
this.showLabelWhenNotEmpty = true})
|
this.showLabelWhenNotEmpty = true});
|
||||||
: super(key,
|
|
||||||
label: label,
|
|
||||||
belowWidgets: belowWidgets,
|
|
||||||
defaultValue: defaultValue,
|
|
||||||
additionalValidators: additionalValidators);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, MapEntry<int, bool>> ensureType(val) {
|
Map<String, MapEntry<int, bool>> ensureType(val) {
|
||||||
return val is Map<String, MapEntry<int, bool>> ? val : {};
|
return val is Map<String, MapEntry<int, bool>> ? val : {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
GeneratedFormTagInput clone() {
|
||||||
|
return GeneratedFormTagInput(key,
|
||||||
|
label: label,
|
||||||
|
belowWidgets: belowWidgets,
|
||||||
|
defaultValue: defaultValue,
|
||||||
|
additionalValidators: List.from(additionalValidators),
|
||||||
|
deleteConfirmationMessage: deleteConfirmationMessage,
|
||||||
|
singleSelect: singleSelect,
|
||||||
|
alignment: alignment,
|
||||||
|
emptyMessage: emptyMessage,
|
||||||
|
showLabelWhenNotEmpty: showLabelWhenNotEmpty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef OnValueChanges = void Function(
|
typedef OnValueChanges = void Function(
|
||||||
@@ -135,6 +175,37 @@ class GeneratedForm extends StatefulWidget {
|
|||||||
State<GeneratedForm> createState() => _GeneratedFormState();
|
State<GeneratedForm> createState() => _GeneratedFormState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<List<GeneratedFormItem>> cloneFormItems(
|
||||||
|
List<List<GeneratedFormItem>> items) {
|
||||||
|
List<List<GeneratedFormItem>> clonedItems = [];
|
||||||
|
for (var row in items) {
|
||||||
|
List<GeneratedFormItem> clonedRow = [];
|
||||||
|
for (var it in row) {
|
||||||
|
clonedRow.add(it.clone());
|
||||||
|
}
|
||||||
|
clonedItems.add(clonedRow);
|
||||||
|
}
|
||||||
|
return clonedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GeneratedFormSubForm extends GeneratedFormItem {
|
||||||
|
final List<List<GeneratedFormItem>> items;
|
||||||
|
|
||||||
|
GeneratedFormSubForm(super.key, this.items,
|
||||||
|
{super.label, super.belowWidgets, super.defaultValue = const []});
|
||||||
|
|
||||||
|
@override
|
||||||
|
ensureType(val) {
|
||||||
|
return val; // Not easy to validate List<Map<String, dynamic>>
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
GeneratedFormSubForm clone() {
|
||||||
|
return GeneratedFormSubForm(key, cloneFormItems(items),
|
||||||
|
label: label, belowWidgets: belowWidgets, defaultValue: defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generates a color in the HSLuv (Pastel) color space
|
// Generates a color in the HSLuv (Pastel) color space
|
||||||
// https://pub.dev/documentation/hsluv/latest/hsluv/Hsluv/hpluvToRgb.html
|
// https://pub.dev/documentation/hsluv/latest/hsluv/Hsluv/hpluvToRgb.html
|
||||||
Color generateRandomLightColor() {
|
Color generateRandomLightColor() {
|
||||||
@@ -151,28 +222,39 @@ Color generateRandomLightColor() {
|
|||||||
return Color.fromARGB(255, rgbValues[0], rgbValues[1], rgbValues[2]);
|
return Color.fromARGB(255, rgbValues[0], rgbValues[1], rgbValues[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int generateRandomNumber(int seed1,
|
||||||
|
{int seed2 = 0, int seed3 = 0, max = 10000}) {
|
||||||
|
int combinedSeed = seed1.hashCode ^ seed2.hashCode ^ seed3.hashCode;
|
||||||
|
Random random = Random(combinedSeed);
|
||||||
|
int randomNumber = random.nextInt(max);
|
||||||
|
return randomNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool validateTextField(TextFormField tf) =>
|
||||||
|
(tf.key as GlobalKey<FormFieldState>).currentState?.isValid == true;
|
||||||
|
|
||||||
class _GeneratedFormState extends State<GeneratedForm> {
|
class _GeneratedFormState extends State<GeneratedForm> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
Map<String, dynamic> values = {};
|
Map<String, dynamic> values = {};
|
||||||
late List<List<Widget>> formInputs;
|
late List<List<Widget>> formInputs;
|
||||||
List<List<Widget>> rows = [];
|
List<List<Widget>> rows = [];
|
||||||
String? initKey;
|
String? initKey;
|
||||||
|
int forceUpdateKeyCount = 0;
|
||||||
|
|
||||||
// If any value changes, call this to update the parent with value and validity
|
// If any value changes, call this to update the parent with value and validity
|
||||||
void someValueChanged({bool isBuilding = false}) {
|
void someValueChanged({bool isBuilding = false, bool forceInvalid = false}) {
|
||||||
Map<String, dynamic> returnValues = values;
|
Map<String, dynamic> returnValues = values;
|
||||||
var valid = true;
|
var valid = true;
|
||||||
for (int r = 0; r < widget.items.length; r++) {
|
for (int r = 0; r < widget.items.length; r++) {
|
||||||
for (int i = 0; i < widget.items[r].length; i++) {
|
for (int i = 0; i < widget.items[r].length; i++) {
|
||||||
if (formInputs[r][i] is TextFormField) {
|
if (formInputs[r][i] is TextFormField) {
|
||||||
var fieldState =
|
valid = valid && validateTextField(formInputs[r][i] as TextFormField);
|
||||||
(formInputs[r][i].key as GlobalKey<FormFieldState>).currentState;
|
|
||||||
if (fieldState != null) {
|
|
||||||
valid = valid && fieldState.isValid;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (forceInvalid) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
widget.onValueChanges(returnValues, valid, isBuilding);
|
widget.onValueChanges(returnValues, valid, isBuilding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,6 +329,17 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
someValueChanged();
|
someValueChanged();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else if (formItem is GeneratedFormSubForm) {
|
||||||
|
values[formItem.key] = [];
|
||||||
|
for (Map<String, dynamic> v
|
||||||
|
in ((formItem.defaultValue ?? []) as List<dynamic>)) {
|
||||||
|
var fullDefaults = getDefaultValuesFromFormItems(formItem.items);
|
||||||
|
for (var element in v.entries) {
|
||||||
|
fullDefaults[element.key] = element.value;
|
||||||
|
}
|
||||||
|
values[formItem.key].add(fullDefaults);
|
||||||
|
}
|
||||||
|
return Container();
|
||||||
} else {
|
} else {
|
||||||
return Container(); // Some input types added in build
|
return Container(); // Some input types added in build
|
||||||
}
|
}
|
||||||
@@ -268,6 +361,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
}
|
}
|
||||||
for (var r = 0; r < formInputs.length; r++) {
|
for (var r = 0; r < formInputs.length; r++) {
|
||||||
for (var e = 0; e < formInputs[r].length; e++) {
|
for (var e = 0; e < formInputs[r].length; e++) {
|
||||||
|
String fieldKey = widget.items[r][e].key;
|
||||||
if (widget.items[r][e] is GeneratedFormSwitch) {
|
if (widget.items[r][e] is GeneratedFormSwitch) {
|
||||||
formInputs[r][e] = Row(
|
formInputs[r][e] = Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
@@ -277,20 +371,55 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
width: 8,
|
width: 8,
|
||||||
),
|
),
|
||||||
Switch(
|
Switch(
|
||||||
value: values[widget.items[r][e].key],
|
value: values[fieldKey],
|
||||||
onChanged: (value) {
|
onChanged:
|
||||||
setState(() {
|
(widget.items[r][e] as GeneratedFormSwitch).disabled
|
||||||
values[widget.items[r][e].key] = value;
|
? null
|
||||||
someValueChanged();
|
: (value) {
|
||||||
});
|
setState(() {
|
||||||
})
|
values[fieldKey] = value;
|
||||||
|
someValueChanged();
|
||||||
|
});
|
||||||
|
})
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else if (widget.items[r][e] is GeneratedFormTagInput) {
|
} else if (widget.items[r][e] is GeneratedFormTagInput) {
|
||||||
|
onAddPressed() {
|
||||||
|
showDialog<Map<String, dynamic>?>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext ctx) {
|
||||||
|
return GeneratedFormModal(
|
||||||
|
title: widget.items[r][e].label,
|
||||||
|
items: [
|
||||||
|
[GeneratedFormTextField('label', label: tr('label'))]
|
||||||
|
]);
|
||||||
|
}).then((value) {
|
||||||
|
String? label = value?['label'];
|
||||||
|
if (label != null) {
|
||||||
|
setState(() {
|
||||||
|
var temp =
|
||||||
|
values[fieldKey] as Map<String, MapEntry<int, bool>>?;
|
||||||
|
temp ??= {};
|
||||||
|
if (temp[label] == null) {
|
||||||
|
var singleSelect =
|
||||||
|
(widget.items[r][e] as GeneratedFormTagInput)
|
||||||
|
.singleSelect;
|
||||||
|
var someSelected = temp.entries
|
||||||
|
.where((element) => element.value.value)
|
||||||
|
.isNotEmpty;
|
||||||
|
temp[label] = MapEntry(generateRandomLightColor().value,
|
||||||
|
!(someSelected && singleSelect));
|
||||||
|
values[fieldKey] = temp;
|
||||||
|
someValueChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
formInputs[r][e] =
|
formInputs[r][e] =
|
||||||
Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
|
Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
|
||||||
if ((values[widget.items[r][e].key]
|
if ((values[fieldKey] as Map<String, MapEntry<int, bool>>?)
|
||||||
as Map<String, MapEntry<int, bool>>?)
|
|
||||||
?.isNotEmpty ==
|
?.isNotEmpty ==
|
||||||
true &&
|
true &&
|
||||||
(widget.items[r][e] as GeneratedFormTagInput)
|
(widget.items[r][e] as GeneratedFormTagInput)
|
||||||
@@ -313,17 +442,15 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
(widget.items[r][e] as GeneratedFormTagInput).alignment,
|
(widget.items[r][e] as GeneratedFormTagInput).alignment,
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
(values[widget.items[r][e].key]
|
// (values[fieldKey] as Map<String, MapEntry<int, bool>>?)
|
||||||
as Map<String, MapEntry<int, bool>>?)
|
// ?.isEmpty ==
|
||||||
?.isEmpty ==
|
// true
|
||||||
true
|
// ? Text(
|
||||||
? Text(
|
// (widget.items[r][e] as GeneratedFormTagInput)
|
||||||
(widget.items[r][e] as GeneratedFormTagInput)
|
// .emptyMessage,
|
||||||
.emptyMessage,
|
// )
|
||||||
)
|
// : const SizedBox.shrink(),
|
||||||
: const SizedBox.shrink(),
|
...(values[fieldKey] as Map<String, MapEntry<int, bool>>?)
|
||||||
...(values[widget.items[r][e].key]
|
|
||||||
as Map<String, MapEntry<int, bool>>?)
|
|
||||||
?.entries
|
?.entries
|
||||||
.map((e2) {
|
.map((e2) {
|
||||||
return Padding(
|
return Padding(
|
||||||
@@ -336,11 +463,10 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
selected: e2.value.value,
|
selected: e2.value.value,
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
(values[widget.items[r][e].key] as Map<String,
|
(values[fieldKey] as Map<String,
|
||||||
MapEntry<int, bool>>)[e2.key] =
|
MapEntry<int, bool>>)[e2.key] =
|
||||||
MapEntry(
|
MapEntry(
|
||||||
(values[widget.items[r][e].key] as Map<
|
(values[fieldKey] as Map<String,
|
||||||
String,
|
|
||||||
MapEntry<int, bool>>)[e2.key]!
|
MapEntry<int, bool>>)[e2.key]!
|
||||||
.key,
|
.key,
|
||||||
value);
|
value);
|
||||||
@@ -348,22 +474,18 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
as GeneratedFormTagInput)
|
as GeneratedFormTagInput)
|
||||||
.singleSelect &&
|
.singleSelect &&
|
||||||
value == true) {
|
value == true) {
|
||||||
for (var key in (values[
|
for (var key in (values[fieldKey]
|
||||||
widget.items[r][e].key]
|
|
||||||
as Map<String, MapEntry<int, bool>>)
|
as Map<String, MapEntry<int, bool>>)
|
||||||
.keys) {
|
.keys) {
|
||||||
if (key != e2.key) {
|
if (key != e2.key) {
|
||||||
(values[widget.items[r][e].key] as Map<
|
(values[fieldKey] as Map<
|
||||||
String,
|
String,
|
||||||
MapEntry<int, bool>>)[key] =
|
MapEntry<int,
|
||||||
MapEntry(
|
bool>>)[key] = MapEntry(
|
||||||
(values[widget.items[r][e].key]
|
(values[fieldKey] as Map<String,
|
||||||
as Map<
|
MapEntry<int, bool>>)[key]!
|
||||||
String,
|
.key,
|
||||||
MapEntry<int,
|
false);
|
||||||
bool>>)[key]!
|
|
||||||
.key,
|
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,8 +495,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
));
|
));
|
||||||
}) ??
|
}) ??
|
||||||
[const SizedBox.shrink()],
|
[const SizedBox.shrink()],
|
||||||
(values[widget.items[r][e].key]
|
(values[fieldKey] as Map<String, MapEntry<int, bool>>?)
|
||||||
as Map<String, MapEntry<int, bool>>?)
|
|
||||||
?.values
|
?.values
|
||||||
.where((e) => e.value)
|
.where((e) => e.value)
|
||||||
.length ==
|
.length ==
|
||||||
@@ -384,7 +505,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
var temp = values[widget.items[r][e].key]
|
var temp = values[fieldKey]
|
||||||
as Map<String, MapEntry<int, bool>>;
|
as Map<String, MapEntry<int, bool>>;
|
||||||
// get selected category str where bool is true
|
// get selected category str where bool is true
|
||||||
final oldEntry = temp.entries
|
final oldEntry = temp.entries
|
||||||
@@ -397,7 +518,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
// Update entry with new color, remain selected
|
// Update entry with new color, remain selected
|
||||||
temp.update(oldEntry.key,
|
temp.update(oldEntry.key,
|
||||||
(old) => MapEntry(newColor, old.value));
|
(old) => MapEntry(newColor, old.value));
|
||||||
values[widget.items[r][e].key] = temp;
|
values[fieldKey] = temp;
|
||||||
someValueChanged();
|
someValueChanged();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -406,8 +527,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
tooltip: tr('colour'),
|
tooltip: tr('colour'),
|
||||||
))
|
))
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
(values[widget.items[r][e].key]
|
(values[fieldKey] as Map<String, MapEntry<int, bool>>?)
|
||||||
as Map<String, MapEntry<int, bool>>?)
|
|
||||||
?.values
|
?.values
|
||||||
.where((e) => e.value)
|
.where((e) => e.value)
|
||||||
.isNotEmpty ==
|
.isNotEmpty ==
|
||||||
@@ -418,10 +538,10 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
fn() {
|
fn() {
|
||||||
setState(() {
|
setState(() {
|
||||||
var temp = values[widget.items[r][e].key]
|
var temp = values[fieldKey]
|
||||||
as Map<String, MapEntry<int, bool>>;
|
as Map<String, MapEntry<int, bool>>;
|
||||||
temp.removeWhere((key, value) => value.value);
|
temp.removeWhere((key, value) => value.value);
|
||||||
values[widget.items[r][e].key] = temp;
|
values[fieldKey] = temp;
|
||||||
someValueChanged();
|
someValueChanged();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -453,52 +573,120 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
tooltip: tr('remove'),
|
tooltip: tr('remove'),
|
||||||
))
|
))
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
Padding(
|
(values[fieldKey] as Map<String, MapEntry<int, bool>>?)
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
?.isEmpty ==
|
||||||
child: IconButton(
|
true
|
||||||
onPressed: () {
|
? Padding(
|
||||||
showDialog<Map<String, dynamic>?>(
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
context: context,
|
child: TextButton.icon(
|
||||||
builder: (BuildContext ctx) {
|
onPressed: onAddPressed,
|
||||||
return GeneratedFormModal(
|
icon: const Icon(Icons.add),
|
||||||
title: widget.items[r][e].label,
|
label: Text(
|
||||||
items: [
|
(widget.items[r][e] as GeneratedFormTagInput)
|
||||||
[
|
.label),
|
||||||
GeneratedFormTextField('label',
|
))
|
||||||
label: tr('label'))
|
: Padding(
|
||||||
]
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
]);
|
child: IconButton(
|
||||||
}).then((value) {
|
onPressed: onAddPressed,
|
||||||
String? label = value?['label'];
|
icon: const Icon(Icons.add),
|
||||||
if (label != null) {
|
visualDensity: VisualDensity.compact,
|
||||||
setState(() {
|
tooltip: tr('add'),
|
||||||
var temp = values[widget.items[r][e].key]
|
)),
|
||||||
as Map<String, MapEntry<int, bool>>?;
|
|
||||||
temp ??= {};
|
|
||||||
if (temp[label] == null) {
|
|
||||||
var singleSelect = (widget.items[r][e]
|
|
||||||
as GeneratedFormTagInput)
|
|
||||||
.singleSelect;
|
|
||||||
var someSelected = temp.entries
|
|
||||||
.where((element) => element.value.value)
|
|
||||||
.isNotEmpty;
|
|
||||||
temp[label] = MapEntry(
|
|
||||||
generateRandomLightColor().value,
|
|
||||||
!(someSelected && singleSelect));
|
|
||||||
values[widget.items[r][e].key] = temp;
|
|
||||||
someValueChanged();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.add),
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
tooltip: tr('add'),
|
|
||||||
)),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
|
} else if (widget.items[r][e] is GeneratedFormSubForm) {
|
||||||
|
List<Widget> subformColumn = [];
|
||||||
|
var compact = (widget.items[r][e] as GeneratedFormSubForm)
|
||||||
|
.items
|
||||||
|
.length ==
|
||||||
|
1 &&
|
||||||
|
(widget.items[r][e] as GeneratedFormSubForm).items[0].length == 1;
|
||||||
|
for (int i = 0; i < values[fieldKey].length; i++) {
|
||||||
|
var internalFormKey = ValueKey(generateRandomNumber(
|
||||||
|
values[fieldKey].length,
|
||||||
|
seed2: i,
|
||||||
|
seed3: forceUpdateKeyCount));
|
||||||
|
subformColumn.add(Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (!compact)
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
if (!compact)
|
||||||
|
Text(
|
||||||
|
'${(widget.items[r][e] as GeneratedFormSubForm).label} (${i + 1})',
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
GeneratedForm(
|
||||||
|
key: internalFormKey,
|
||||||
|
items: cloneFormItems(
|
||||||
|
(widget.items[r][e] as GeneratedFormSubForm).items)
|
||||||
|
.map((x) => x.map((y) {
|
||||||
|
y.defaultValue = values[fieldKey]?[i]?[y.key];
|
||||||
|
y.key = '${y.key.toString()},$internalFormKey';
|
||||||
|
return y;
|
||||||
|
}).toList())
|
||||||
|
.toList(),
|
||||||
|
onValueChanges: (values, valid, isBuilding) {
|
||||||
|
values = values.map(
|
||||||
|
(key, value) => MapEntry(key.split(',')[0], value));
|
||||||
|
if (valid) {
|
||||||
|
this.values[fieldKey]?[i] = values;
|
||||||
|
}
|
||||||
|
someValueChanged(
|
||||||
|
isBuilding: isBuilding, forceInvalid: !valid);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
TextButton.icon(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor:
|
||||||
|
Theme.of(context).colorScheme.error),
|
||||||
|
onPressed: (values[fieldKey].length > 0)
|
||||||
|
? () {
|
||||||
|
var temp = List.from(values[fieldKey]);
|
||||||
|
temp.removeAt(i);
|
||||||
|
values[fieldKey] = List.from(temp);
|
||||||
|
forceUpdateKeyCount++;
|
||||||
|
someValueChanged();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
label: Text(
|
||||||
|
'${(widget.items[r][e] as GeneratedFormSubForm).label} (${i + 1})',
|
||||||
|
),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.delete_outline_rounded,
|
||||||
|
))
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
subformColumn.add(Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 0, top: 8),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
values[fieldKey].add(getDefaultValuesFromFormItems(
|
||||||
|
(widget.items[r][e] as GeneratedFormSubForm)
|
||||||
|
.items));
|
||||||
|
forceUpdateKeyCount++;
|
||||||
|
someValueChanged();
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
label: Text((widget.items[r][e] as GeneratedFormSubForm)
|
||||||
|
.label))),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
formInputs[r][e] = Column(children: subformColumn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:android_package_installer/android_package_installer.dart';
|
import 'package:android_package_installer/android_package_installer.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -32,7 +34,9 @@ class CredsNeededError extends ObtainiumError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NoReleasesError extends ObtainiumError {
|
class NoReleasesError extends ObtainiumError {
|
||||||
NoReleasesError() : super(tr('noReleaseFound'));
|
NoReleasesError({String? note})
|
||||||
|
: super(
|
||||||
|
'${tr('noReleaseFound')}${note?.isNotEmpty == true ? '\n\n$note' : ''}');
|
||||||
}
|
}
|
||||||
|
|
||||||
class NoAPKError extends ObtainiumError {
|
class NoAPKError extends ObtainiumError {
|
||||||
@@ -65,30 +69,43 @@ class NotImplementedError extends ObtainiumError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MultiAppMultiError extends ObtainiumError {
|
class MultiAppMultiError extends ObtainiumError {
|
||||||
Map<String, List<String>> content = {};
|
Map<String, dynamic> rawErrors = {};
|
||||||
|
Map<String, List<String>> idsByErrorString = {};
|
||||||
|
Map<String, String> appIdNames = {};
|
||||||
|
|
||||||
MultiAppMultiError() : super(tr('placeholder'), unexpected: true);
|
MultiAppMultiError() : super(tr('placeholder'), unexpected: true);
|
||||||
|
|
||||||
add(String appId, String string) {
|
add(String appId, dynamic error, {String? appName}) {
|
||||||
var tempIds = content.remove(string);
|
if (error is SocketException) {
|
||||||
|
error = error.message;
|
||||||
|
}
|
||||||
|
rawErrors[appId] = error;
|
||||||
|
var string = error.toString();
|
||||||
|
var tempIds = idsByErrorString.remove(string);
|
||||||
tempIds ??= [];
|
tempIds ??= [];
|
||||||
tempIds.add(appId);
|
tempIds.add(appId);
|
||||||
content.putIfAbsent(string, () => tempIds!);
|
idsByErrorString.putIfAbsent(string, () => tempIds!);
|
||||||
|
if (appName != null) {
|
||||||
|
appIdNames[appId] = appName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String errorString(String appId, {bool includeIdsWithNames = false}) =>
|
||||||
|
'${appIdNames.containsKey(appId) ? '${appIdNames[appId]}${includeIdsWithNames ? ' ($appId)' : ''}' : appId}: ${rawErrors[appId].toString()}';
|
||||||
|
|
||||||
|
String errorsAppsString(String errString, List<String> appIds,
|
||||||
|
{bool includeIdsWithNames = false}) =>
|
||||||
|
'$errString [${list2FriendlyString(appIds.map((id) => appIdNames.containsKey(id) == true ? '${appIdNames[id]}${includeIdsWithNames ? ' ($id)' : ''}' : id).toList())}]';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() => idsByErrorString.entries
|
||||||
String finalString = '';
|
.map((e) => errorsAppsString(e.key, e.value))
|
||||||
for (var e in content.keys) {
|
.join('\n\n');
|
||||||
finalString += '$e: ${content[e].toString()}\n\n';
|
|
||||||
}
|
|
||||||
return finalString;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showError(dynamic e, BuildContext context) {
|
showMessage(dynamic e, BuildContext context, {bool isError = false}) {
|
||||||
Provider.of<LogsProvider>(context, listen: false)
|
Provider.of<LogsProvider>(context, listen: false)
|
||||||
.add(e.toString(), level: LogLevels.error);
|
.add(e.toString(), level: isError ? LogLevels.error : LogLevels.info);
|
||||||
if (e is String || (e is ObtainiumError && !e.unexpected)) {
|
if (e is String || (e is ObtainiumError && !e.unexpected)) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text(e.toString())),
|
SnackBar(content: Text(e.toString())),
|
||||||
@@ -100,8 +117,8 @@ showError(dynamic e, BuildContext context) {
|
|||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
scrollable: true,
|
scrollable: true,
|
||||||
title: Text(e is MultiAppMultiError
|
title: Text(e is MultiAppMultiError
|
||||||
? tr('someErrors')
|
? tr(isError ? 'someErrors' : 'updates')
|
||||||
: tr('unexpectedError')),
|
: tr(isError ? 'unexpectedError' : 'unknown')),
|
||||||
content: GestureDetector(
|
content: GestureDetector(
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
Clipboard.setData(ClipboardData(text: e.toString()));
|
Clipboard.setData(ClipboardData(text: e.toString()));
|
||||||
@@ -122,6 +139,10 @@ showError(dynamic e, BuildContext context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showError(dynamic e, BuildContext context) {
|
||||||
|
showMessage(e, context, isError: true);
|
||||||
|
}
|
||||||
|
|
||||||
String list2FriendlyString(List<String> list) {
|
String list2FriendlyString(List<String> list) {
|
||||||
return list.length == 2
|
return list.length == 2
|
||||||
? '${list[0]} ${tr('and')} ${list[1]}'
|
? '${list[0]} ${tr('and')} ${list[1]}'
|
||||||
|
|||||||
127
lib/main.dart
@@ -12,19 +12,13 @@ import 'package:permission_handler/permission_handler.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
|
import 'package:background_fetch/background_fetch.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
// ignore: implementation_imports
|
// ignore: implementation_imports
|
||||||
import 'package:easy_localization/src/easy_localization_controller.dart';
|
import 'package:easy_localization/src/easy_localization_controller.dart';
|
||||||
// ignore: implementation_imports
|
// ignore: implementation_imports
|
||||||
import 'package:easy_localization/src/localization.dart';
|
import 'package:easy_localization/src/localization.dart';
|
||||||
|
|
||||||
const String currentVersion = '0.14.19';
|
|
||||||
const String currentReleaseTag =
|
|
||||||
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
|
||||||
|
|
||||||
const int bgUpdateCheckAlarmId = 666;
|
|
||||||
|
|
||||||
List<MapEntry<Locale, String>> supportedLocales = const [
|
List<MapEntry<Locale, String>> supportedLocales = const [
|
||||||
MapEntry(Locale('en'), 'English'),
|
MapEntry(Locale('en'), 'English'),
|
||||||
MapEntry(Locale('zh'), '简体中文'),
|
MapEntry(Locale('zh'), '简体中文'),
|
||||||
@@ -36,13 +30,18 @@ List<MapEntry<Locale, String>> supportedLocales = const [
|
|||||||
MapEntry(Locale('fr'), 'Français'),
|
MapEntry(Locale('fr'), 'Français'),
|
||||||
MapEntry(Locale('es'), 'Español'),
|
MapEntry(Locale('es'), 'Español'),
|
||||||
MapEntry(Locale('pl'), 'Polski'),
|
MapEntry(Locale('pl'), 'Polski'),
|
||||||
MapEntry(Locale('ru'), 'Русский язык'),
|
MapEntry(Locale('ru'), 'Русский'),
|
||||||
MapEntry(Locale('bs'), 'Bosanski'),
|
MapEntry(Locale('bs'), 'Bosanski'),
|
||||||
MapEntry(Locale('pt'), 'Brasileiro'),
|
MapEntry(Locale('pt'), 'Brasileiro'),
|
||||||
MapEntry(Locale('cs'), 'Česky'),
|
MapEntry(Locale('cs'), 'Česky'),
|
||||||
|
MapEntry(Locale('sv'), 'Svenska'),
|
||||||
|
MapEntry(Locale('nl'), 'Nederlands'),
|
||||||
|
MapEntry(Locale('vi'), 'Tiếng Việt'),
|
||||||
|
MapEntry(Locale('tr'), 'Türkçe'),
|
||||||
];
|
];
|
||||||
const fallbackLocale = Locale('en');
|
const fallbackLocale = Locale('en');
|
||||||
const localeDir = 'assets/translations';
|
const localeDir = 'assets/translations';
|
||||||
|
var fdroid = false;
|
||||||
|
|
||||||
final globalNavigatorKey = GlobalKey<NavigatorState>();
|
final globalNavigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
@@ -71,6 +70,19 @@ Future<void> loadTranslations() async {
|
|||||||
fallbackTranslations: controller.fallbackTranslations);
|
fallbackTranslations: controller.fallbackTranslations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@pragma('vm:entry-point')
|
||||||
|
void backgroundFetchHeadlessTask(HeadlessTask task) async {
|
||||||
|
String taskId = task.taskId;
|
||||||
|
bool isTimeout = task.timeout;
|
||||||
|
if (isTimeout) {
|
||||||
|
print('BG update task timed out.');
|
||||||
|
BackgroundFetch.finish(taskId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await bgUpdateCheck(taskId, null);
|
||||||
|
BackgroundFetch.finish(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
try {
|
try {
|
||||||
@@ -88,7 +100,6 @@ void main() async {
|
|||||||
);
|
);
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||||
}
|
}
|
||||||
await AndroidAlarmManager.initialize();
|
|
||||||
runApp(MultiProvider(
|
runApp(MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider(create: (context) => AppsProvider()),
|
ChangeNotifierProvider(create: (context) => AppsProvider()),
|
||||||
@@ -103,6 +114,7 @@ void main() async {
|
|||||||
useOnlyLangCode: true,
|
useOnlyLangCode: true,
|
||||||
child: const Obtainium()),
|
child: const Obtainium()),
|
||||||
));
|
));
|
||||||
|
BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultThemeColour = Colors.deepPurple;
|
var defaultThemeColour = Colors.deepPurple;
|
||||||
@@ -117,6 +129,33 @@ class Obtainium extends StatefulWidget {
|
|||||||
class _ObtainiumState extends State<Obtainium> {
|
class _ObtainiumState extends State<Obtainium> {
|
||||||
var existingUpdateInterval = -1;
|
var existingUpdateInterval = -1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
initPlatformState();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initPlatformState() async {
|
||||||
|
await BackgroundFetch.configure(
|
||||||
|
BackgroundFetchConfig(
|
||||||
|
minimumFetchInterval: 15,
|
||||||
|
stopOnTerminate: false,
|
||||||
|
startOnBoot: true,
|
||||||
|
enableHeadless: true,
|
||||||
|
requiresBatteryNotLow: false,
|
||||||
|
requiresCharging: false,
|
||||||
|
requiresStorageNotLow: false,
|
||||||
|
requiresDeviceIdle: false,
|
||||||
|
requiredNetworkType: NetworkType.ANY), (String taskId) async {
|
||||||
|
await bgUpdateCheck(taskId, null);
|
||||||
|
BackgroundFetch.finish(taskId);
|
||||||
|
}, (String taskId) async {
|
||||||
|
context.read<LogsProvider>().add('BG update task timed out.');
|
||||||
|
BackgroundFetch.finish(taskId);
|
||||||
|
});
|
||||||
|
if (!mounted) return;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
||||||
@@ -131,20 +170,32 @@ class _ObtainiumState extends State<Obtainium> {
|
|||||||
logs.add('This is the first ever run of Obtainium.');
|
logs.add('This is the first ever run of Obtainium.');
|
||||||
// If this is the first run, ask for notification permissions and add Obtainium to the Apps list
|
// If this is the first run, ask for notification permissions and add Obtainium to the Apps list
|
||||||
Permission.notification.request();
|
Permission.notification.request();
|
||||||
appsProvider.saveApps([
|
if (!fdroid) {
|
||||||
App(
|
getInstalledInfo(obtainiumId).then((value) {
|
||||||
obtainiumId,
|
if (value?.versionName != null) {
|
||||||
'https://github.com/ImranR98/Obtainium',
|
appsProvider.saveApps([
|
||||||
'ImranR98',
|
App(
|
||||||
'Obtainium',
|
obtainiumId,
|
||||||
currentReleaseTag,
|
obtainiumUrl,
|
||||||
currentReleaseTag,
|
'ImranR98',
|
||||||
[],
|
'Obtainium',
|
||||||
0,
|
value!.versionName,
|
||||||
{'includePrereleases': true},
|
value.versionName!,
|
||||||
null,
|
[],
|
||||||
false)
|
0,
|
||||||
], onlyIfExists: false);
|
{
|
||||||
|
'versionDetection': true,
|
||||||
|
'apkFilterRegEx': 'fdroid',
|
||||||
|
'invertAPKFilter': true
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
false)
|
||||||
|
], onlyIfExists: false);
|
||||||
|
}
|
||||||
|
}).catchError((err) {
|
||||||
|
print(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!supportedLocales
|
if (!supportedLocales
|
||||||
.map((e) => e.key.languageCode)
|
.map((e) => e.key.languageCode)
|
||||||
@@ -154,30 +205,6 @@ class _ObtainiumState extends State<Obtainium> {
|
|||||||
context.locale.languageCode)) {
|
context.locale.languageCode)) {
|
||||||
settingsProvider.resetLocaleSafe(context);
|
settingsProvider.resetLocaleSafe(context);
|
||||||
}
|
}
|
||||||
// Register the background update task according to the user's setting
|
|
||||||
var actualUpdateInterval = settingsProvider.updateInterval;
|
|
||||||
if (existingUpdateInterval != actualUpdateInterval) {
|
|
||||||
if (actualUpdateInterval == 0) {
|
|
||||||
AndroidAlarmManager.cancel(bgUpdateCheckAlarmId);
|
|
||||||
} else {
|
|
||||||
var settingChanged = existingUpdateInterval != -1;
|
|
||||||
var lastCheckWasTooLongAgo = actualUpdateInterval != 0 &&
|
|
||||||
settingsProvider.lastBGCheckTime
|
|
||||||
.add(Duration(minutes: actualUpdateInterval + 60))
|
|
||||||
.isBefore(DateTime.now());
|
|
||||||
if (settingChanged || lastCheckWasTooLongAgo) {
|
|
||||||
logs.add(
|
|
||||||
'Update interval was set to ${actualUpdateInterval.toString()} (reason: ${settingChanged ? 'setting changed' : 'last check was ${settingsProvider.lastBGCheckTime.toLocal().toString()}'}).');
|
|
||||||
AndroidAlarmManager.periodic(
|
|
||||||
Duration(minutes: actualUpdateInterval),
|
|
||||||
bgUpdateCheckAlarmId,
|
|
||||||
bgUpdateCheck,
|
|
||||||
rescheduleOnReboot: true,
|
|
||||||
wakeup: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
existingUpdateInterval = actualUpdateInterval;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return DynamicColorBuilder(
|
return DynamicColorBuilder(
|
||||||
@@ -214,13 +241,15 @@ class _ObtainiumState extends State<Obtainium> {
|
|||||||
colorScheme: settingsProvider.theme == ThemeSettings.dark
|
colorScheme: settingsProvider.theme == ThemeSettings.dark
|
||||||
? darkColorScheme
|
? darkColorScheme
|
||||||
: lightColorScheme,
|
: lightColorScheme,
|
||||||
fontFamily: 'Metropolis'),
|
fontFamily:
|
||||||
|
settingsProvider.useSystemFont ? 'SystemFont' : 'Metropolis'),
|
||||||
darkTheme: ThemeData(
|
darkTheme: ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
colorScheme: settingsProvider.theme == ThemeSettings.light
|
colorScheme: settingsProvider.theme == ThemeSettings.light
|
||||||
? lightColorScheme
|
? lightColorScheme
|
||||||
: darkColorScheme,
|
: darkColorScheme,
|
||||||
fontFamily: 'Metropolis'),
|
fontFamily:
|
||||||
|
settingsProvider.useSystemFont ? 'SystemFont' : 'Metropolis'),
|
||||||
home: Shortcuts(shortcuts: <LogicalKeySet, Intent>{
|
home: Shortcuts(shortcuts: <LogicalKeySet, Intent>{
|
||||||
LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
|
LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
|
||||||
}, child: const HomePage()));
|
}, child: const HomePage()));
|
||||||
|
|||||||
6
lib/main_fdroid.dart
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import 'main.dart' as m;
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
m.fdroid = true;
|
||||||
|
m.main();
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ class GitHubStars implements MassAppUrlSource {
|
|||||||
Response res = await get(
|
Response res = await get(
|
||||||
Uri.parse(
|
Uri.parse(
|
||||||
'https://api.github.com/users/$username/starred?per_page=100&page=$page'),
|
'https://api.github.com/users/$username/starred?per_page=100&page=$page'),
|
||||||
headers: await GitHub().getRequestHeaders());
|
headers: await GitHub().getRequestHeaders({}));
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
Map<String, List<String>> urlsWithDescriptions = {};
|
Map<String, List<String>> urlsWithDescriptions = {};
|
||||||
for (var e in (jsonDecode(res.body) as List<dynamic>)) {
|
for (var e in (jsonDecode(res.body) as List<dynamic>)) {
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ class AddAppPage extends StatefulWidget {
|
|||||||
const AddAppPage({super.key});
|
const AddAppPage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AddAppPage> createState() => _AddAppPageState();
|
State<AddAppPage> createState() => AddAppPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AddAppPageState extends State<AddAppPage> {
|
class AddAppPageState extends State<AddAppPage> {
|
||||||
bool gettingAppInfo = false;
|
bool gettingAppInfo = false;
|
||||||
bool searching = false;
|
bool searching = false;
|
||||||
|
|
||||||
@@ -36,9 +36,64 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
bool additionalSettingsValid = true;
|
bool additionalSettingsValid = true;
|
||||||
bool inferAppIdIfOptional = true;
|
bool inferAppIdIfOptional = true;
|
||||||
List<String> pickedCategories = [];
|
List<String> pickedCategories = [];
|
||||||
int searchnum = 0;
|
int urlInputKey = 0;
|
||||||
SourceProvider sourceProvider = SourceProvider();
|
SourceProvider sourceProvider = SourceProvider();
|
||||||
|
|
||||||
|
linkFn(String input) {
|
||||||
|
try {
|
||||||
|
if (input.isEmpty) {
|
||||||
|
throw UnsupportedURLError();
|
||||||
|
}
|
||||||
|
sourceProvider.getSource(input);
|
||||||
|
changeUserInput(input, true, false, updateUrlInput: true);
|
||||||
|
} catch (e) {
|
||||||
|
showError(e, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changeUserInput(String input, bool valid, bool isBuilding,
|
||||||
|
{bool updateUrlInput = false}) {
|
||||||
|
userInput = input;
|
||||||
|
if (!isBuilding) {
|
||||||
|
setState(() {
|
||||||
|
if (updateUrlInput) {
|
||||||
|
urlInputKey++;
|
||||||
|
}
|
||||||
|
var prevHost = pickedSource?.hosts.isNotEmpty == true
|
||||||
|
? pickedSource?.hosts[0]
|
||||||
|
: null;
|
||||||
|
try {
|
||||||
|
var naturalSource =
|
||||||
|
valid ? sourceProvider.getSource(userInput) : null;
|
||||||
|
if (naturalSource != null &&
|
||||||
|
naturalSource.runtimeType.toString() !=
|
||||||
|
HTML().runtimeType.toString()) {
|
||||||
|
// If input has changed to match a regular source, reset the override
|
||||||
|
pickedSourceOverride = null;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
var source = valid
|
||||||
|
? sourceProvider.getSource(userInput,
|
||||||
|
overrideSource: pickedSourceOverride)
|
||||||
|
: null;
|
||||||
|
if (pickedSource.runtimeType != source.runtimeType ||
|
||||||
|
(prevHost != null && prevHost != source?.hosts[0])) {
|
||||||
|
pickedSource = source;
|
||||||
|
additionalSettings = source != null
|
||||||
|
? getDefaultValuesFromFormItems(
|
||||||
|
source.combinedAppSpecificSettingFormItems)
|
||||||
|
: {};
|
||||||
|
additionalSettingsValid = source != null
|
||||||
|
? !sourceProvider.ifRequiredAppSpecificSettingsExist(source)
|
||||||
|
: true;
|
||||||
|
inferAppIdIfOptional = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
AppsProvider appsProvider = context.read<AppsProvider>();
|
AppsProvider appsProvider = context.read<AppsProvider>();
|
||||||
@@ -48,47 +103,6 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
|
|
||||||
bool doingSomething = gettingAppInfo || searching;
|
bool doingSomething = gettingAppInfo || searching;
|
||||||
|
|
||||||
changeUserInput(String input, bool valid, bool isBuilding,
|
|
||||||
{bool isSearch = false}) {
|
|
||||||
userInput = input;
|
|
||||||
if (!isBuilding) {
|
|
||||||
setState(() {
|
|
||||||
if (isSearch) {
|
|
||||||
searchnum++;
|
|
||||||
}
|
|
||||||
var prevHost = pickedSource?.host;
|
|
||||||
try {
|
|
||||||
var naturalSource =
|
|
||||||
valid ? sourceProvider.getSource(userInput) : null;
|
|
||||||
if (naturalSource != null &&
|
|
||||||
naturalSource.runtimeType.toString() !=
|
|
||||||
HTML().runtimeType.toString()) {
|
|
||||||
// If input has changed to match a regular source, reset the override
|
|
||||||
pickedSourceOverride = null;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
var source = valid
|
|
||||||
? sourceProvider.getSource(userInput,
|
|
||||||
overrideSource: pickedSourceOverride)
|
|
||||||
: null;
|
|
||||||
if (pickedSource.runtimeType != source.runtimeType ||
|
|
||||||
(prevHost != null && prevHost != source?.host)) {
|
|
||||||
pickedSource = source;
|
|
||||||
additionalSettings = source != null
|
|
||||||
? getDefaultValuesFromFormItems(
|
|
||||||
source.combinedAppSpecificSettingFormItems)
|
|
||||||
: {};
|
|
||||||
additionalSettingsValid = source != null
|
|
||||||
? !sourceProvider.ifRequiredAppSpecificSettingsExist(source)
|
|
||||||
: true;
|
|
||||||
inferAppIdIfOptional = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> getTrackOnlyConfirmationIfNeeded(bool userPickedTrackOnly,
|
Future<bool> getTrackOnlyConfirmationIfNeeded(bool userPickedTrackOnly,
|
||||||
{bool ignoreHideSetting = false}) async {
|
{bool ignoreHideSetting = false}) async {
|
||||||
var useTrackOnly = userPickedTrackOnly || pickedSource!.enforceTrackOnly;
|
var useTrackOnly = userPickedTrackOnly || pickedSource!.enforceTrackOnly;
|
||||||
@@ -121,8 +135,7 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
|
|
||||||
getReleaseDateAsVersionConfirmationIfNeeded(
|
getReleaseDateAsVersionConfirmationIfNeeded(
|
||||||
bool userPickedTrackOnly) async {
|
bool userPickedTrackOnly) async {
|
||||||
return (!(additionalSettings['versionDetection'] ==
|
return (!(additionalSettings['releaseDateAsVersion'] == true &&
|
||||||
'releaseDateAsVersion' &&
|
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -153,8 +166,7 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
overrideSource: pickedSourceOverride,
|
overrideSource: pickedSourceOverride,
|
||||||
inferAppIdIfOptional: inferAppIdIfOptional);
|
inferAppIdIfOptional: inferAppIdIfOptional);
|
||||||
// Only download the APK here if you need to for the package ID
|
// Only download the APK here if you need to for the package ID
|
||||||
if (sourceProvider.isTempId(app) &&
|
if (isTempId(app) && app.additionalSettings['trackOnly'] != true) {
|
||||||
app.additionalSettings['trackOnly'] != true) {
|
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
var apkUrl = await appsProvider.confirmApkUrl(app, context);
|
var apkUrl = await appsProvider.confirmApkUrl(app, context);
|
||||||
if (apkUrl == null) {
|
if (apkUrl == null) {
|
||||||
@@ -179,8 +191,7 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
throw ObtainiumError(tr('appAlreadyAdded'));
|
throw ObtainiumError(tr('appAlreadyAdded'));
|
||||||
}
|
}
|
||||||
if (app.additionalSettings['trackOnly'] == true ||
|
if (app.additionalSettings['trackOnly'] == true ||
|
||||||
app.additionalSettings['versionDetection'] !=
|
app.additionalSettings['versionDetection'] != true) {
|
||||||
'standardVersionDetection') {
|
|
||||||
app.installedVersion = app.latestVersion;
|
app.installedVersion = app.latestVersion;
|
||||||
}
|
}
|
||||||
app.categories = pickedCategories;
|
app.categories = pickedCategories;
|
||||||
@@ -206,7 +217,7 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GeneratedForm(
|
child: GeneratedForm(
|
||||||
key: Key(searchnum.toString()),
|
key: Key(urlInputKey.toString()),
|
||||||
items: [
|
items: [
|
||||||
[
|
[
|
||||||
GeneratedFormTextField('appSourceURL',
|
GeneratedFormTextField('appSourceURL',
|
||||||
@@ -255,56 +266,82 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
runSearch() async {
|
runSearch({bool filtered = true}) async {
|
||||||
setState(() {
|
setState(() {
|
||||||
searching = true;
|
searching = true;
|
||||||
});
|
});
|
||||||
|
var sourceStrings = <String, List<String>>{};
|
||||||
|
sourceProvider.sources
|
||||||
|
.where((e) => e.canSearch && !e.excludeFromMassSearch)
|
||||||
|
.forEach((s) {
|
||||||
|
sourceStrings[s.name] = [s.name];
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
var results = await Future.wait(
|
var searchSources = await showDialog<List<String>?>(
|
||||||
sourceProvider.sources.where((e) => e.canSearch).map((e) async {
|
|
||||||
try {
|
|
||||||
return await e.search(searchQuery);
|
|
||||||
} catch (err) {
|
|
||||||
if (err is! CredsNeededError) {
|
|
||||||
rethrow;
|
|
||||||
} else {
|
|
||||||
return <String, List<String>>{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// .then((results) async {
|
|
||||||
// Interleave results instead of simple reduce
|
|
||||||
Map<String, List<String>> res = {};
|
|
||||||
var si = 0;
|
|
||||||
var done = false;
|
|
||||||
while (!done) {
|
|
||||||
done = true;
|
|
||||||
for (var r in results) {
|
|
||||||
if (r.length > si) {
|
|
||||||
done = false;
|
|
||||||
res.addEntries([r.entries.elementAt(si)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
si++;
|
|
||||||
}
|
|
||||||
if (res.isEmpty) {
|
|
||||||
throw ObtainiumError(tr('noResults'));
|
|
||||||
}
|
|
||||||
List<String>? selectedUrls = res.isEmpty
|
|
||||||
? []
|
|
||||||
// ignore: use_build_context_synchronously
|
|
||||||
: await showDialog<List<String>?>(
|
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext ctx) {
|
builder: (BuildContext ctx) {
|
||||||
return UrlSelectionModal(
|
return SelectionModal(
|
||||||
urlsWithDescriptions: res,
|
title: tr('selectX', args: [plural('source', 2)]),
|
||||||
selectedByDefault: false,
|
entries: sourceStrings,
|
||||||
onlyOneSelectionAllowed: true,
|
selectedByDefault: true,
|
||||||
|
onlyOneSelectionAllowed: false,
|
||||||
|
titlesAreLinks: false,
|
||||||
|
deselectThese: settingsProvider.searchDeselected,
|
||||||
);
|
);
|
||||||
});
|
}) ??
|
||||||
if (selectedUrls != null && selectedUrls.isNotEmpty) {
|
[];
|
||||||
changeUserInput(selectedUrls[0], true, false, isSearch: true);
|
if (searchSources.isNotEmpty) {
|
||||||
|
settingsProvider.searchDeselected = sourceStrings.keys
|
||||||
|
.where((s) => !searchSources.contains(s))
|
||||||
|
.toList();
|
||||||
|
var results = await Future.wait(sourceProvider.sources
|
||||||
|
.where((e) => searchSources.contains(e.name))
|
||||||
|
.map((e) async {
|
||||||
|
try {
|
||||||
|
return await e.search(searchQuery);
|
||||||
|
} catch (err) {
|
||||||
|
if (err is! CredsNeededError) {
|
||||||
|
rethrow;
|
||||||
|
} else {
|
||||||
|
err.unexpected = true;
|
||||||
|
showError(err, context);
|
||||||
|
return <String, List<String>>{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Interleave results instead of simple reduce
|
||||||
|
Map<String, List<String>> res = {};
|
||||||
|
var si = 0;
|
||||||
|
var done = false;
|
||||||
|
while (!done) {
|
||||||
|
done = true;
|
||||||
|
for (var r in results) {
|
||||||
|
if (r.length > si) {
|
||||||
|
done = false;
|
||||||
|
res.addEntries([r.entries.elementAt(si)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
si++;
|
||||||
|
}
|
||||||
|
if (res.isEmpty) {
|
||||||
|
throw ObtainiumError(tr('noResults'));
|
||||||
|
}
|
||||||
|
List<String>? selectedUrls = res.isEmpty
|
||||||
|
? []
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
: await showDialog<List<String>?>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext ctx) {
|
||||||
|
return SelectionModal(
|
||||||
|
entries: res,
|
||||||
|
selectedByDefault: false,
|
||||||
|
onlyOneSelectionAllowed: true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (selectedUrls != null && selectedUrls.isNotEmpty) {
|
||||||
|
changeUserInput(selectedUrls[0], true, false, updateUrlInput: true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showError(e, context);
|
showError(e, context);
|
||||||
@@ -459,38 +496,74 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget getSourcesListWidget() => Column(
|
Widget getSourcesListWidget() => Padding(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
padding: const EdgeInsets.all(16),
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
GestureDetector(
|
||||||
tr('supportedSources'),
|
onTap: () {
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
showDialog(
|
||||||
),
|
context: context,
|
||||||
const SizedBox(
|
builder: (context) {
|
||||||
height: 16,
|
return GeneratedFormModal(
|
||||||
),
|
singleNullReturnButton: tr('ok'),
|
||||||
...sourceProvider.sources
|
title: tr('supportedSources'),
|
||||||
.map((e) => GestureDetector(
|
items: const [],
|
||||||
onTap: e.host != null
|
additionalWidgets: [
|
||||||
? () {
|
...sourceProvider.sources.map(
|
||||||
launchUrlString('https://${e.host}',
|
(e) => Padding(
|
||||||
mode: LaunchMode.externalApplication);
|
padding:
|
||||||
}
|
const EdgeInsets.symmetric(vertical: 4),
|
||||||
: null,
|
child: GestureDetector(
|
||||||
child: Text(
|
onTap: e.hosts.isNotEmpty
|
||||||
'${e.name}${e.enforceTrackOnly ? ' ${tr('trackOnlyInBrackets')}' : ''}${e.canSearch ? ' ${tr('searchableInBrackets')}' : ''}',
|
? () {
|
||||||
style: TextStyle(
|
launchUrlString(
|
||||||
decoration: e.host != null
|
'https://${e.hosts[0]}',
|
||||||
? TextDecoration.underline
|
mode: LaunchMode
|
||||||
: TextDecoration.none,
|
.externalApplication);
|
||||||
fontStyle: FontStyle.italic),
|
}
|
||||||
)))
|
: null,
|
||||||
.toList()
|
child: Text(
|
||||||
]);
|
'${e.name}${e.enforceTrackOnly ? ' ${tr('trackOnlyInBrackets')}' : ''}${e.canSearch ? ' ${tr('searchableInBrackets')}' : ''}',
|
||||||
|
style: TextStyle(
|
||||||
|
decoration: e.hosts.isNotEmpty
|
||||||
|
? TextDecoration.underline
|
||||||
|
: TextDecoration.none),
|
||||||
|
))),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'${tr('note')}:',
|
||||||
|
style:
|
||||||
|
const TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Text(tr('selfHostedNote',
|
||||||
|
args: [tr('overrideSource')])),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
tr('supportedSources'),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
fontStyle: FontStyle.italic),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
|
bottomNavigationBar:
|
||||||
|
pickedSource == null ? getSourcesListWidget() : null,
|
||||||
body: CustomScrollView(shrinkWrap: true, slivers: <Widget>[
|
body: CustomScrollView(shrinkWrap: true, slivers: <Widget>[
|
||||||
CustomAppBar(title: tr('addApp')),
|
CustomAppBar(title: tr('addApp')),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
@@ -522,18 +595,7 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
: const SizedBox();
|
: const SizedBox();
|
||||||
},
|
},
|
||||||
future: pickedSource?.getSourceNote()),
|
future: pickedSource?.getSourceNote()),
|
||||||
SizedBox(
|
|
||||||
height: pickedSource != null ? 16 : 96,
|
|
||||||
),
|
|
||||||
if (pickedSource != null) getAdditionalOptsCol(),
|
if (pickedSource != null) getAdditionalOptsCol(),
|
||||||
if (pickedSource == null)
|
|
||||||
const Divider(
|
|
||||||
height: 48,
|
|
||||||
),
|
|
||||||
if (pickedSource == null) getSourcesListWidget(),
|
|
||||||
SizedBox(
|
|
||||||
height: pickedSource != null ? 8 : 2,
|
|
||||||
),
|
|
||||||
])),
|
])),
|
||||||
)
|
)
|
||||||
]));
|
]));
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:obtainium/components/generated_form.dart';
|
|
||||||
import 'package:obtainium/components/generated_form_modal.dart';
|
import 'package:obtainium/components/generated_form_modal.dart';
|
||||||
import 'package:obtainium/custom_errors.dart';
|
import 'package:obtainium/custom_errors.dart';
|
||||||
import 'package:obtainium/main.dart';
|
import 'package:obtainium/main.dart';
|
||||||
@@ -24,16 +23,35 @@ class AppPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _AppPageState extends State<AppPage> {
|
class _AppPageState extends State<AppPage> {
|
||||||
AppInMemory? prevApp;
|
AppInMemory? prevApp;
|
||||||
|
bool updating = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var appsProvider = context.watch<AppsProvider>();
|
var appsProvider = context.watch<AppsProvider>();
|
||||||
var settingsProvider = context.watch<SettingsProvider>();
|
var settingsProvider = context.watch<SettingsProvider>();
|
||||||
getUpdate(String id) {
|
getUpdate(String id, {bool resetVersion = false}) async {
|
||||||
appsProvider.checkUpdate(id).catchError((e) {
|
try {
|
||||||
showError(e, context);
|
setState(() {
|
||||||
return null;
|
updating = true;
|
||||||
});
|
});
|
||||||
|
await appsProvider.checkUpdate(id);
|
||||||
|
if (resetVersion) {
|
||||||
|
appsProvider.apps[id]?.app.additionalSettings['versionDetection'] =
|
||||||
|
true;
|
||||||
|
if (appsProvider.apps[id]?.app.installedVersion != null) {
|
||||||
|
appsProvider.apps[id]?.app.installedVersion =
|
||||||
|
appsProvider.apps[id]?.app.latestVersion;
|
||||||
|
}
|
||||||
|
appsProvider.saveApps([appsProvider.apps[id]!.app]);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
showError(err, context);
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
updating = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool areDownloadsRunning = appsProvider.areDownloadsRunning();
|
bool areDownloadsRunning = appsProvider.areDownloadsRunning();
|
||||||
@@ -54,18 +72,145 @@ class _AppPageState extends State<AppPage> {
|
|||||||
var trackOnly = app?.app.additionalSettings['trackOnly'] == true;
|
var trackOnly = app?.app.additionalSettings['trackOnly'] == true;
|
||||||
|
|
||||||
bool isVersionDetectionStandard =
|
bool isVersionDetectionStandard =
|
||||||
app?.app.additionalSettings['versionDetection'] ==
|
app?.app.additionalSettings['versionDetection'] == true;
|
||||||
'standardVersionDetection';
|
|
||||||
|
|
||||||
bool installedVersionIsEstimate = trackOnly ||
|
bool installedVersionIsEstimate = trackOnly ||
|
||||||
(app?.app.installedVersion != null &&
|
(app?.app.installedVersion != null &&
|
||||||
app?.app.additionalSettings['versionDetection'] ==
|
app?.app.additionalSettings['versionDetection'] != true);
|
||||||
'noVersionDetection');
|
|
||||||
|
|
||||||
getInfoColumn() => Column(
|
getInfoColumn() {
|
||||||
|
String versionLines = '';
|
||||||
|
bool installed = app?.app.installedVersion != null;
|
||||||
|
bool upToDate = app?.app.installedVersion == app?.app.latestVersion;
|
||||||
|
if (installed) {
|
||||||
|
versionLines = '${app?.app.installedVersion} ${tr('installed')}';
|
||||||
|
if (upToDate) {
|
||||||
|
versionLines += '/${tr('latest')}';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
versionLines = tr('notInstalled');
|
||||||
|
}
|
||||||
|
if (!upToDate) {
|
||||||
|
versionLines += '\n${app?.app.latestVersion} ${tr('latest')}';
|
||||||
|
}
|
||||||
|
String infoLines = tr('lastUpdateCheckX', args: [
|
||||||
|
app?.app.lastUpdateCheck == null
|
||||||
|
? tr('never')
|
||||||
|
: '${app?.app.lastUpdateCheck?.toLocal()}'
|
||||||
|
]);
|
||||||
|
if (trackOnly) {
|
||||||
|
infoLines = '${tr('xIsTrackOnly', args: [tr('app')])}\n$infoLines';
|
||||||
|
}
|
||||||
|
if (installedVersionIsEstimate) {
|
||||||
|
infoLines = '${tr('pseudoVersionInUse')}\n$infoLines';
|
||||||
|
}
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 24),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Text(versionLines,
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyLarge!
|
||||||
|
.copyWith(fontWeight: FontWeight.bold)),
|
||||||
|
app?.app.releaseDate == null
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: Text(
|
||||||
|
app!.app.releaseDate.toString(),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.labelSmall,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
infoLines,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(fontStyle: FontStyle.italic, fontSize: 12),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 48,
|
||||||
|
),
|
||||||
|
CategoryEditorSelector(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
preselected: app?.app.categories != null
|
||||||
|
? app!.app.categories.toSet()
|
||||||
|
: {},
|
||||||
|
onSelected: (categories) {
|
||||||
|
if (app != null) {
|
||||||
|
app.app.categories = categories;
|
||||||
|
appsProvider.saveApps([app.app]);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
if (app?.app.additionalSettings['about'] is String &&
|
||||||
|
app?.app.additionalSettings['about'].isNotEmpty)
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 48,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onLongPress: () {
|
||||||
|
Clipboard.setData(ClipboardData(
|
||||||
|
text: app?.app.additionalSettings['about'] ?? ''));
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
|
content: Text(tr('copiedToClipboard')),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
app?.app.additionalSettings['about'],
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(fontStyle: FontStyle.italic),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFullInfoColumn() => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
app?.icon != null
|
||||||
|
? Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||||
|
GestureDetector(
|
||||||
|
child: Image.memory(
|
||||||
|
app!.icon!,
|
||||||
|
height: 150,
|
||||||
|
gaplessPlayback: true,
|
||||||
|
),
|
||||||
|
onTap: () => pm.openApp(app.app.id),
|
||||||
|
)
|
||||||
|
])
|
||||||
|
: Container(),
|
||||||
|
const SizedBox(
|
||||||
|
height: 25,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
app?.name ?? tr('app'),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.displayLarge,
|
||||||
|
),
|
||||||
|
Text(tr('byX', args: [app?.app.author ?? tr('unknown')]),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.headlineMedium),
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (app?.app.url != null) {
|
if (app?.app.url != null) {
|
||||||
@@ -82,117 +227,15 @@ class _AppPageState extends State<AppPage> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
app?.app.url ?? '',
|
app?.app.url ?? '',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: Theme.of(context).textTheme.labelSmall!.copyWith(
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
fontStyle: FontStyle.italic,
|
fontStyle: FontStyle.italic),
|
||||||
fontSize: 12),
|
|
||||||
)),
|
)),
|
||||||
const SizedBox(
|
|
||||||
height: 32,
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'${tr('latestVersionX', args: [
|
|
||||||
app?.app.latestVersion ?? tr('unknown')
|
|
||||||
])}\n${tr('installedVersionX', args: [
|
|
||||||
app?.app.installedVersion ?? tr('none')
|
|
||||||
])}${installedVersionIsEstimate ? '\n${tr('estimateInBrackets')}' : ''}',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: Theme.of(context).textTheme.bodyLarge!,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (app?.app.installedVersion != null &&
|
|
||||||
!isVersionDetectionStandard)
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
const SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'${trackOnly ? '${tr('xIsTrackOnly', args: [
|
|
||||||
tr('app')
|
|
||||||
])}\n' : ''}${tr('noVersionDetection')}',
|
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 32,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
tr('lastUpdateCheckX', args: [
|
|
||||||
app?.app.lastUpdateCheck == null
|
|
||||||
? tr('never')
|
|
||||||
: '\n${app?.app.lastUpdateCheck?.toLocal()}'
|
|
||||||
]),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: const TextStyle(fontStyle: FontStyle.italic, fontSize: 12),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 48,
|
|
||||||
),
|
|
||||||
CategoryEditorSelector(
|
|
||||||
alignment: WrapAlignment.center,
|
|
||||||
preselected: app?.app.categories != null
|
|
||||||
? app!.app.categories.toSet()
|
|
||||||
: {},
|
|
||||||
onSelected: (categories) {
|
|
||||||
if (app != null) {
|
|
||||||
app.app.categories = categories;
|
|
||||||
appsProvider.saveApps([app.app]);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
getFullInfoColumn() => Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
app?.icon != null
|
|
||||||
? Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
|
||||||
Image.memory(
|
|
||||||
app!.icon!,
|
|
||||||
height: 150,
|
|
||||||
gaplessPlayback: true,
|
|
||||||
)
|
|
||||||
])
|
|
||||||
: Container(),
|
|
||||||
const SizedBox(
|
|
||||||
height: 25,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
app?.name ?? tr('app'),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: Theme.of(context).textTheme.displayLarge,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
tr('byX', args: [app?.app.author ?? tr('unknown')]),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 8,
|
|
||||||
),
|
|
||||||
Text(
|
Text(
|
||||||
app?.app.id ?? '',
|
app?.app.id ?? '',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
style: Theme.of(context).textTheme.labelSmall,
|
||||||
),
|
),
|
||||||
app?.app.releaseDate == null
|
|
||||||
? const SizedBox.shrink()
|
|
||||||
: Text(
|
|
||||||
app!.app.releaseDate.toString(),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 32,
|
|
||||||
),
|
|
||||||
getInfoColumn(),
|
getInfoColumn(),
|
||||||
const SizedBox(height: 150)
|
const SizedBox(height: 150)
|
||||||
],
|
],
|
||||||
@@ -261,25 +304,6 @@ class _AppPageState extends State<AppPage> {
|
|||||||
return row;
|
return row;
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
items = items.map((row) {
|
|
||||||
row = row.map((e) {
|
|
||||||
if (e.key == 'versionDetection' && e is GeneratedFormDropdown) {
|
|
||||||
e.disabledOptKeys ??= [];
|
|
||||||
if (app?.app.installedVersion != null &&
|
|
||||||
app?.app.additionalSettings['versionDetection'] !=
|
|
||||||
'releaseDateAsVersion' &&
|
|
||||||
!appsProvider.isVersionDetectionPossible(app)) {
|
|
||||||
e.disabledOptKeys!.add('standardVersionDetection');
|
|
||||||
}
|
|
||||||
if (app?.app.releaseDate == null) {
|
|
||||||
e.disabledOptKeys!.add('releaseDateAsVersion');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}).toList();
|
|
||||||
return row;
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
return GeneratedFormModal(
|
return GeneratedFormModal(
|
||||||
title: tr('additionalOptions'), items: items);
|
title: tr('additionalOptions'), items: items);
|
||||||
});
|
});
|
||||||
@@ -292,46 +316,43 @@ class _AppPageState extends State<AppPage> {
|
|||||||
if (source?.enforceTrackOnly == true) {
|
if (source?.enforceTrackOnly == true) {
|
||||||
app.app.additionalSettings['trackOnly'] = true;
|
app.app.additionalSettings['trackOnly'] = true;
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
showError(tr('appsFromSourceAreTrackOnly'), context);
|
showMessage(tr('appsFromSourceAreTrackOnly'), context);
|
||||||
}
|
}
|
||||||
if (app.app.additionalSettings['versionDetection'] ==
|
var versionDetectionEnabled =
|
||||||
'releaseDateAsVersion') {
|
app.app.additionalSettings['versionDetection'] == true &&
|
||||||
if (originalSettings['versionDetection'] != 'releaseDateAsVersion') {
|
originalSettings['versionDetection'] != true;
|
||||||
if (app.app.releaseDate != null) {
|
var releaseDateVersionEnabled =
|
||||||
bool isUpdated =
|
app.app.additionalSettings['releaseDateAsVersion'] == true &&
|
||||||
app.app.installedVersion == app.app.latestVersion;
|
originalSettings['releaseDateAsVersion'] != true;
|
||||||
app.app.latestVersion =
|
var releaseDateVersionDisabled =
|
||||||
app.app.releaseDate!.microsecondsSinceEpoch.toString();
|
app.app.additionalSettings['releaseDateAsVersion'] != true &&
|
||||||
if (isUpdated) {
|
originalSettings['releaseDateAsVersion'] == true;
|
||||||
app.app.installedVersion = app.app.latestVersion;
|
if (releaseDateVersionEnabled) {
|
||||||
}
|
if (app.app.releaseDate != null) {
|
||||||
|
bool isUpdated = app.app.installedVersion == app.app.latestVersion;
|
||||||
|
app.app.latestVersion =
|
||||||
|
app.app.releaseDate!.microsecondsSinceEpoch.toString();
|
||||||
|
if (isUpdated) {
|
||||||
|
app.app.installedVersion = app.app.latestVersion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (originalSettings['versionDetection'] ==
|
} else if (releaseDateVersionDisabled) {
|
||||||
'releaseDateAsVersion') {
|
|
||||||
app.app.installedVersion =
|
app.app.installedVersion =
|
||||||
app.installedInfo?.versionName ?? app.app.installedVersion;
|
app.installedInfo?.versionName ?? app.app.installedVersion;
|
||||||
}
|
}
|
||||||
|
if (versionDetectionEnabled) {
|
||||||
|
app.app.additionalSettings['versionDetection'] = true;
|
||||||
|
app.app.additionalSettings['releaseDateAsVersion'] = false;
|
||||||
|
}
|
||||||
appsProvider.saveApps([app.app]).then((value) {
|
appsProvider.saveApps([app.app]).then((value) {
|
||||||
getUpdate(app.app.id);
|
getUpdate(app.app.id, resetVersion: versionDetectionEnabled);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getResetInstallStatusButton() => TextButton(
|
|
||||||
onPressed: app?.app == null
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
app!.app.installedVersion = null;
|
|
||||||
appsProvider.saveApps([app.app]);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
tr('resetInstallStatus'),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
));
|
|
||||||
|
|
||||||
getInstallOrUpdateButton() => TextButton(
|
getInstallOrUpdateButton() => TextButton(
|
||||||
onPressed: (app?.app.installedVersion == null ||
|
onPressed: !updating &&
|
||||||
|
(app?.app.installedVersion == null ||
|
||||||
app?.app.installedVersion != app?.app.latestVersion) &&
|
app?.app.installedVersion != app?.app.latestVersion) &&
|
||||||
!areDownloadsRunning
|
!areDownloadsRunning
|
||||||
? () async {
|
? () async {
|
||||||
@@ -341,9 +362,9 @@ class _AppPageState extends State<AppPage> {
|
|||||||
app?.app.id != null ? [app!.app.id] : [],
|
app?.app.id != null ? [app!.app.id] : [],
|
||||||
globalNavigatorKey.currentContext,
|
globalNavigatorKey.currentContext,
|
||||||
);
|
);
|
||||||
if (app?.app.installedVersion != null && !trackOnly) {
|
if (res.isNotEmpty && !trackOnly) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
showError(tr('appsUpdated'), context);
|
showMessage(tr('appsUpdated'), context);
|
||||||
}
|
}
|
||||||
if (res.isNotEmpty && mounted) {
|
if (res.isNotEmpty && mounted) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
@@ -373,20 +394,10 @@ class _AppPageState extends State<AppPage> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
if (app?.app.installedVersion != null &&
|
|
||||||
app?.app.installedVersion != app?.app.latestVersion &&
|
|
||||||
!isVersionDetectionStandard &&
|
|
||||||
!trackOnly)
|
|
||||||
IconButton(
|
|
||||||
onPressed: app?.downloadProgress != null
|
|
||||||
? null
|
|
||||||
: showMarkUpdatedDialog,
|
|
||||||
tooltip: tr('markUpdated'),
|
|
||||||
icon: const Icon(Icons.done)),
|
|
||||||
if (source != null &&
|
if (source != null &&
|
||||||
source.combinedAppSpecificSettingFormItems.isNotEmpty)
|
source.combinedAppSpecificSettingFormItems.isNotEmpty)
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: app?.downloadProgress != null
|
onPressed: app?.downloadProgress != null || updating
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
var values =
|
var values =
|
||||||
@@ -428,17 +439,33 @@ class _AppPageState extends State<AppPage> {
|
|||||||
},
|
},
|
||||||
icon: const Icon(Icons.more_horiz),
|
icon: const Icon(Icons.more_horiz),
|
||||||
tooltip: tr('more')),
|
tooltip: tr('more')),
|
||||||
|
if (app?.app.installedVersion != null &&
|
||||||
|
app?.app.installedVersion != app?.app.latestVersion &&
|
||||||
|
!isVersionDetectionStandard &&
|
||||||
|
!trackOnly)
|
||||||
|
IconButton(
|
||||||
|
onPressed: app?.downloadProgress != null || updating
|
||||||
|
? null
|
||||||
|
: showMarkUpdatedDialog,
|
||||||
|
tooltip: tr('markUpdated'),
|
||||||
|
icon: const Icon(Icons.done)),
|
||||||
|
if ((!isVersionDetectionStandard || trackOnly) &&
|
||||||
|
app?.app.installedVersion != null &&
|
||||||
|
app?.app.installedVersion == app?.app.latestVersion)
|
||||||
|
IconButton(
|
||||||
|
onPressed: app?.app == null || updating
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
app!.app.installedVersion = null;
|
||||||
|
appsProvider.saveApps([app.app]);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.restore_rounded),
|
||||||
|
tooltip: tr('resetInstallStatus')),
|
||||||
const SizedBox(width: 16.0),
|
const SizedBox(width: 16.0),
|
||||||
Expanded(
|
Expanded(child: getInstallOrUpdateButton()),
|
||||||
child: (!isVersionDetectionStandard || trackOnly) &&
|
|
||||||
app?.app.installedVersion != null &&
|
|
||||||
app?.app.installedVersion ==
|
|
||||||
app?.app.latestVersion
|
|
||||||
? getResetInstallStatusButton()
|
|
||||||
: getInstallOrUpdateButton()),
|
|
||||||
const SizedBox(width: 16.0),
|
const SizedBox(width: 16.0),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: app?.downloadProgress != null
|
onPressed: app?.downloadProgress != null || updating
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
appsProvider
|
appsProvider
|
||||||
@@ -463,15 +490,15 @@ class _AppPageState extends State<AppPage> {
|
|||||||
: null))
|
: null))
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
|
||||||
appScreenAppBar() => AppBar(
|
appScreenAppBar() => AppBar(
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.arrow_back),
|
icon: const Icon(Icons.arrow_back),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: settingsProvider.showAppWebpage ? AppBar() : appScreenAppBar(),
|
appBar: settingsProvider.showAppWebpage ? AppBar() : appScreenAppBar(),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@@ -68,7 +70,7 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
refreshingSince = DateTime.now();
|
refreshingSince = DateTime.now();
|
||||||
});
|
});
|
||||||
return appsProvider.checkUpdates().catchError((e) {
|
return appsProvider.checkUpdates().catchError((e) {
|
||||||
showError(e, context);
|
showError(e is Map ? e['errors'] : e, context);
|
||||||
return <App>[];
|
return <App>[];
|
||||||
}).whenComplete(() {
|
}).whenComplete(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -356,6 +358,16 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
String? changesUrl =
|
String? changesUrl =
|
||||||
appSource.changeLogPageFromStandardUrl(listedApps[appIndex].app.url);
|
appSource.changeLogPageFromStandardUrl(listedApps[appIndex].app.url);
|
||||||
String? changeLog = listedApps[appIndex].app.changeLog;
|
String? changeLog = listedApps[appIndex].app.changeLog;
|
||||||
|
if (changeLog?.split('\n').length == 1) {
|
||||||
|
if (RegExp(
|
||||||
|
'(http|ftp|https)://([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?')
|
||||||
|
.hasMatch(changeLog!)) {
|
||||||
|
if (changesUrl == null) {
|
||||||
|
changesUrl = changeLog;
|
||||||
|
changeLog = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return (changeLog == null && changesUrl == null)
|
return (changeLog == null && changesUrl == null)
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
@@ -419,7 +431,7 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getVersionText(int appIndex) {
|
getVersionText(int appIndex) {
|
||||||
return '${listedApps[appIndex].app.installedVersion ?? tr('notInstalled')}${listedApps[appIndex].app.additionalSettings['trackOnly'] == true ? ' ${tr('estimateInBrackets')}' : ''}';
|
return '${listedApps[appIndex].app.installedVersion ?? tr('notInstalled')}${listedApps[appIndex].app.additionalSettings['trackOnly'] == true ? ' ${tr('pseudoVersion')}' : ''}';
|
||||||
}
|
}
|
||||||
|
|
||||||
getChangesButtonString(int appIndex, bool hasChangeLogFn) {
|
getChangesButtonString(int appIndex, bool hasChangeLogFn) {
|
||||||
@@ -496,14 +508,8 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
var transparent =
|
var transparent =
|
||||||
Theme.of(context).colorScheme.background.withAlpha(0).value;
|
Theme.of(context).colorScheme.background.withAlpha(0).value;
|
||||||
List<double> stops = [
|
List<double> stops = [
|
||||||
...listedApps[index]
|
...listedApps[index].app.categories.asMap().entries.map(
|
||||||
.app
|
(e) => ((e.key / (listedApps[index].app.categories.length - 1)))),
|
||||||
.categories
|
|
||||||
.asMap()
|
|
||||||
.entries
|
|
||||||
.map((e) =>
|
|
||||||
((e.key / (listedApps[index].app.categories.length - 1))))
|
|
||||||
.toList(),
|
|
||||||
1
|
1
|
||||||
];
|
];
|
||||||
if (stops.length == 2) {
|
if (stops.length == 2) {
|
||||||
@@ -516,13 +522,9 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
begin: const Alignment(-1, 0),
|
begin: const Alignment(-1, 0),
|
||||||
end: const Alignment(-0.97, 0),
|
end: const Alignment(-0.97, 0),
|
||||||
colors: [
|
colors: [
|
||||||
...listedApps[index]
|
...listedApps[index].app.categories.map((e) =>
|
||||||
.app
|
Color(settingsProvider.categories[e] ?? transparent)
|
||||||
.categories
|
.withAlpha(255)),
|
||||||
.map((e) =>
|
|
||||||
Color(settingsProvider.categories[e] ?? transparent)
|
|
||||||
.withAlpha(255))
|
|
||||||
.toList(),
|
|
||||||
Color(transparent)
|
Color(transparent)
|
||||||
])),
|
])),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
@@ -566,7 +568,7 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
.toInt()
|
.toInt()
|
||||||
.toString()
|
.toString()
|
||||||
])
|
])
|
||||||
: tr('pleaseWait'),
|
: tr('installing'),
|
||||||
textAlign: (listedApps[index].downloadProgress! >= 0)
|
textAlign: (listedApps[index].downloadProgress! >= 0)
|
||||||
? TextAlign.start
|
? TextAlign.start
|
||||||
: TextAlign.end,
|
: TextAlign.end,
|
||||||
@@ -704,8 +706,8 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
showError(e, context);
|
showError(e, context);
|
||||||
return <String>[];
|
return <String>[];
|
||||||
}).then((value) {
|
}).then((value) {
|
||||||
if (shouldInstallUpdates) {
|
if (value.isNotEmpty && shouldInstallUpdates) {
|
||||||
showError(tr('appsUpdated'), context);
|
showMessage(tr('appsUpdated'), context);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -833,7 +835,7 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
items: const [],
|
items: const [],
|
||||||
initValid: true,
|
initValid: true,
|
||||||
message: tr('installStatusOfXWillBeResetExplanation',
|
message: tr('installStatusOfXWillBeResetExplanation',
|
||||||
args: [plural('app', selectedAppIds.length)]),
|
args: [plural('apps', selectedAppIds.length)]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
if (values != null) {
|
if (values != null) {
|
||||||
@@ -885,16 +887,38 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
}
|
}
|
||||||
urls = urls.substring(0, urls.length - 1);
|
urls = urls.substring(0, urls.length - 1);
|
||||||
Share.share(urls,
|
Share.share(urls,
|
||||||
subject: tr('selectedAppURLsFromObtainium'));
|
subject: 'Obtainium - ${tr('appsString')}');
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
tooltip: tr('shareSelectedAppURLs'),
|
tooltip: tr('shareSelectedAppURLs'),
|
||||||
icon: const Icon(Icons.share),
|
icon: const Icon(Icons.share_rounded),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: resetSelectedAppsInstallStatuses,
|
onPressed: selectedAppIds.isEmpty
|
||||||
tooltip: tr('resetInstallStatus'),
|
? null
|
||||||
icon: const Icon(Icons.restore_page_outlined),
|
: () {
|
||||||
|
String urls =
|
||||||
|
'<p>${tr('customLinkMessage')}:</p>\n\n<ul>\n';
|
||||||
|
for (var a in selectedApps) {
|
||||||
|
urls +=
|
||||||
|
' <li><a href="obtainium://app/${Uri.encodeComponent(jsonEncode({
|
||||||
|
'id': a.id,
|
||||||
|
'url': a.url,
|
||||||
|
'author': a.author,
|
||||||
|
'name': a.name,
|
||||||
|
'preferredApkIndex':
|
||||||
|
a.preferredApkIndex,
|
||||||
|
'additionalSettings':
|
||||||
|
jsonEncode(a.additionalSettings)
|
||||||
|
}))}">${a.name}</a></li>\n';
|
||||||
|
}
|
||||||
|
urls +=
|
||||||
|
'</ul>\n\n<p><a href="$obtainiumUrl">${tr('about')}</a></p>';
|
||||||
|
Share.share(urls,
|
||||||
|
subject: 'Obtainium - ${tr('appsString')}');
|
||||||
|
},
|
||||||
|
tooltip: tr('shareAppConfigLinks'),
|
||||||
|
icon: const Icon(Icons.ios_share),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
@@ -981,10 +1005,8 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
defaultValue: filter.sourceFilter,
|
defaultValue: filter.sourceFilter,
|
||||||
[
|
[
|
||||||
MapEntry('', tr('none')),
|
MapEntry('', tr('none')),
|
||||||
...sourceProvider.sources
|
...sourceProvider.sources.map(
|
||||||
.map((e) =>
|
(e) => MapEntry(e.runtimeType.toString(), e.name))
|
||||||
MapEntry(e.runtimeType.toString(), e.name))
|
|
||||||
.toList()
|
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:animations/animations.dart';
|
import 'package:animations/animations.dart';
|
||||||
|
import 'package:app_links/app_links.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:obtainium/components/generated_form_modal.dart';
|
||||||
|
import 'package:obtainium/custom_errors.dart';
|
||||||
import 'package:obtainium/pages/add_app.dart';
|
import 'package:obtainium/pages/add_app.dart';
|
||||||
import 'package:obtainium/pages/apps.dart';
|
import 'package:obtainium/pages/apps.dart';
|
||||||
import 'package:obtainium/pages/import_export.dart';
|
import 'package:obtainium/pages/import_export.dart';
|
||||||
@@ -30,58 +35,144 @@ class _HomePageState extends State<HomePage> {
|
|||||||
bool isReversing = false;
|
bool isReversing = false;
|
||||||
int prevAppCount = -1;
|
int prevAppCount = -1;
|
||||||
bool prevIsLoading = true;
|
bool prevIsLoading = true;
|
||||||
|
late AppLinks _appLinks;
|
||||||
|
StreamSubscription<Uri>? _linkSubscription;
|
||||||
|
bool isLinkActivity = false;
|
||||||
|
|
||||||
List<NavigationPageItem> pages = [
|
List<NavigationPageItem> pages = [
|
||||||
NavigationPageItem(tr('appsString'), Icons.apps,
|
NavigationPageItem(tr('appsString'), Icons.apps,
|
||||||
AppsPage(key: GlobalKey<AppsPageState>())),
|
AppsPage(key: GlobalKey<AppsPageState>())),
|
||||||
NavigationPageItem(tr('addApp'), Icons.add, const AddAppPage()),
|
NavigationPageItem(
|
||||||
|
tr('addApp'), Icons.add, AddAppPage(key: GlobalKey<AddAppPageState>())),
|
||||||
NavigationPageItem(
|
NavigationPageItem(
|
||||||
tr('importExport'), Icons.import_export, const ImportExportPage()),
|
tr('importExport'), Icons.import_export, const ImportExportPage()),
|
||||||
NavigationPageItem(tr('settings'), Icons.settings, const SettingsPage())
|
NavigationPageItem(tr('settings'), Icons.settings, const SettingsPage())
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
initDeepLinks();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initDeepLinks() async {
|
||||||
|
_appLinks = AppLinks();
|
||||||
|
|
||||||
|
goToAddApp(String data) async {
|
||||||
|
switchToPage(1);
|
||||||
|
while (
|
||||||
|
(pages[1].widget.key as GlobalKey<AddAppPageState>?)?.currentState ==
|
||||||
|
null) {
|
||||||
|
await Future.delayed(const Duration(microseconds: 1));
|
||||||
|
}
|
||||||
|
(pages[1].widget.key as GlobalKey<AddAppPageState>?)
|
||||||
|
?.currentState
|
||||||
|
?.linkFn(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
interpretLink(Uri uri) async {
|
||||||
|
isLinkActivity = true;
|
||||||
|
var action = uri.host;
|
||||||
|
var data = uri.path.length > 1 ? uri.path.substring(1) : "";
|
||||||
|
try {
|
||||||
|
if (action == 'add') {
|
||||||
|
await goToAddApp(data);
|
||||||
|
} else if (action == 'app' || action == 'apps') {
|
||||||
|
var dataStr = Uri.decodeComponent(data);
|
||||||
|
if (await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext ctx) {
|
||||||
|
return GeneratedFormModal(
|
||||||
|
title: tr('importX', args: [
|
||||||
|
action == 'app' ? tr('app') : tr('appsString')
|
||||||
|
]),
|
||||||
|
items: const [],
|
||||||
|
additionalWidgets: [
|
||||||
|
ExpansionTile(
|
||||||
|
title: const Text('Raw JSON'),
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
dataStr,
|
||||||
|
style: const TextStyle(fontFamily: 'monospace'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}) !=
|
||||||
|
null) {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
var result = await context.read<AppsProvider>().import(
|
||||||
|
action == 'app'
|
||||||
|
? '{ "apps": [$dataStr] }'
|
||||||
|
: '{ "apps": $dataStr }');
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
showMessage(
|
||||||
|
tr('importedX', args: [plural('apps', result.key)]), context);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw ObtainiumError(tr('unknown'));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showError(e, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check initial link if app was in cold state (terminated)
|
||||||
|
final appLink = await _appLinks.getInitialAppLink();
|
||||||
|
if (appLink != null) {
|
||||||
|
await interpretLink(appLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle link when app is in warm state (front or background)
|
||||||
|
_linkSubscription = _appLinks.uriLinkStream.listen((uri) async {
|
||||||
|
await interpretLink(uri);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsReversing(int targetIndex) {
|
||||||
|
bool reversing = selectedIndexHistory.isNotEmpty &&
|
||||||
|
selectedIndexHistory.last > targetIndex;
|
||||||
|
setState(() {
|
||||||
|
isReversing = reversing;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
switchToPage(int index) async {
|
||||||
|
setIsReversing(index);
|
||||||
|
if (index == 0) {
|
||||||
|
while ((pages[0].widget.key as GlobalKey<AppsPageState>).currentState !=
|
||||||
|
null) {
|
||||||
|
// Avoid duplicate GlobalKey error
|
||||||
|
await Future.delayed(const Duration(microseconds: 1));
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
selectedIndexHistory.clear();
|
||||||
|
});
|
||||||
|
} else if (selectedIndexHistory.isEmpty ||
|
||||||
|
(selectedIndexHistory.isNotEmpty &&
|
||||||
|
selectedIndexHistory.last != index)) {
|
||||||
|
setState(() {
|
||||||
|
int existingInd = selectedIndexHistory.indexOf(index);
|
||||||
|
if (existingInd >= 0) {
|
||||||
|
selectedIndexHistory.removeAt(existingInd);
|
||||||
|
}
|
||||||
|
selectedIndexHistory.add(index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
AppsProvider appsProvider = context.watch<AppsProvider>();
|
AppsProvider appsProvider = context.watch<AppsProvider>();
|
||||||
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
||||||
|
|
||||||
setIsReversing(int targetIndex) {
|
|
||||||
bool reversing = selectedIndexHistory.isNotEmpty &&
|
|
||||||
selectedIndexHistory.last > targetIndex;
|
|
||||||
setState(() {
|
|
||||||
isReversing = reversing;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
switchToPage(int index) async {
|
|
||||||
setIsReversing(index);
|
|
||||||
if (index == 0) {
|
|
||||||
while ((pages[0].widget.key as GlobalKey<AppsPageState>).currentState !=
|
|
||||||
null) {
|
|
||||||
// Avoid duplicate GlobalKey error
|
|
||||||
await Future.delayed(const Duration(microseconds: 1));
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
selectedIndexHistory.clear();
|
|
||||||
});
|
|
||||||
} else if (selectedIndexHistory.isEmpty ||
|
|
||||||
(selectedIndexHistory.isNotEmpty &&
|
|
||||||
selectedIndexHistory.last != index)) {
|
|
||||||
setState(() {
|
|
||||||
int existingInd = selectedIndexHistory.indexOf(index);
|
|
||||||
if (existingInd >= 0) {
|
|
||||||
selectedIndexHistory.removeAt(existingInd);
|
|
||||||
}
|
|
||||||
selectedIndexHistory.add(index);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!prevIsLoading &&
|
if (!prevIsLoading &&
|
||||||
prevAppCount >= 0 &&
|
prevAppCount >= 0 &&
|
||||||
appsProvider.apps.length > prevAppCount &&
|
appsProvider.apps.length > prevAppCount &&
|
||||||
selectedIndexHistory.isNotEmpty &&
|
selectedIndexHistory.isNotEmpty &&
|
||||||
selectedIndexHistory.last == 1) {
|
selectedIndexHistory.last == 1 &&
|
||||||
|
!isLinkActivity) {
|
||||||
switchToPage(0);
|
switchToPage(0);
|
||||||
}
|
}
|
||||||
prevAppCount = appsProvider.apps.length;
|
prevAppCount = appsProvider.apps.length;
|
||||||
@@ -129,6 +220,11 @@ class _HomePageState extends State<HomePage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onWillPop: () async {
|
onWillPop: () async {
|
||||||
|
if (isLinkActivity &&
|
||||||
|
selectedIndexHistory.length == 1 &&
|
||||||
|
selectedIndexHistory.last == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
setIsReversing(selectedIndexHistory.length >= 2
|
setIsReversing(selectedIndexHistory.length >= 2
|
||||||
? selectedIndexHistory.reversed.toList()[1]
|
? selectedIndexHistory.reversed.toList()[1]
|
||||||
: 0);
|
: 0);
|
||||||
@@ -143,4 +239,10 @@ class _HomePageState extends State<HomePage> {
|
|||||||
?.clearSelected();
|
?.clearSelected();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_linkSubscription?.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'dart:io';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:obtainium/app_sources/fdroidrepo.dart';
|
||||||
import 'package:obtainium/components/custom_app_bar.dart';
|
import 'package:obtainium/components/custom_app_bar.dart';
|
||||||
import 'package:obtainium/components/generated_form.dart';
|
import 'package:obtainium/components/generated_form.dart';
|
||||||
import 'package:obtainium/components/generated_form_modal.dart';
|
import 'package:obtainium/components/generated_form_modal.dart';
|
||||||
@@ -81,7 +82,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
});
|
});
|
||||||
appsProvider.addAppsByURL(urls).then((errors) {
|
appsProvider.addAppsByURL(urls).then((errors) {
|
||||||
if (errors.isEmpty) {
|
if (errors.isEmpty) {
|
||||||
showError(tr('importedX', args: [plural('apps', urls.length)]),
|
showMessage(tr('importedX', args: [plural('apps', urls.length)]),
|
||||||
context);
|
context);
|
||||||
} else {
|
} else {
|
||||||
showDialog(
|
showDialog(
|
||||||
@@ -102,15 +103,16 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
runObtainiumExport() async {
|
runObtainiumExport({bool pickOnly = false}) async {
|
||||||
HapticFeedback.selectionClick();
|
HapticFeedback.selectionClick();
|
||||||
appsProvider
|
appsProvider
|
||||||
.exportApps(
|
.export(
|
||||||
pickOnly: (await settingsProvider.getExportDir()) == null,
|
pickOnly:
|
||||||
|
pickOnly || (await settingsProvider.getExportDir()) == null,
|
||||||
sp: settingsProvider)
|
sp: settingsProvider)
|
||||||
.then((String? result) {
|
.then((String? result) {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
showError(tr('exportedTo', args: [result]), context);
|
showMessage(tr('exportedTo', args: [result]), context);
|
||||||
}
|
}
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
showError(e, context);
|
showError(e, context);
|
||||||
@@ -130,7 +132,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw ObtainiumError(tr('invalidInput'));
|
throw ObtainiumError(tr('invalidInput'));
|
||||||
}
|
}
|
||||||
appsProvider.importApps(data).then((value) {
|
appsProvider.import(data).then((value) {
|
||||||
var cats = settingsProvider.categories;
|
var cats = settingsProvider.categories;
|
||||||
appsProvider.apps.forEach((key, value) {
|
appsProvider.apps.forEach((key, value) {
|
||||||
for (var c in value.app.categories) {
|
for (var c in value.app.categories) {
|
||||||
@@ -140,7 +142,11 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
appsProvider.addMissingCategories(settingsProvider);
|
appsProvider.addMissingCategories(settingsProvider);
|
||||||
showError(tr('importedX', args: [plural('apps', value)]), context);
|
showMessage(
|
||||||
|
'${tr('importedX', args: [
|
||||||
|
plural('apps', value.key)
|
||||||
|
])}${value.value ? ' + ${tr('settings')}' : ''}',
|
||||||
|
context);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// User canceled the picker
|
// User canceled the picker
|
||||||
@@ -187,17 +193,30 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
items: [
|
items: [
|
||||||
[
|
[
|
||||||
GeneratedFormTextField('searchQuery',
|
GeneratedFormTextField('searchQuery',
|
||||||
label: tr('searchQuery'))
|
label: tr('searchQuery'),
|
||||||
|
required: source.name != FDroidRepo().name)
|
||||||
|
],
|
||||||
|
...source.searchQuerySettingFormItems.map((e) => [e]),
|
||||||
|
[
|
||||||
|
GeneratedFormTextField('url',
|
||||||
|
label: source.hosts.isNotEmpty
|
||||||
|
? tr('overrideSource')
|
||||||
|
: plural('url', 1).substring(2),
|
||||||
|
defaultValue:
|
||||||
|
source.hosts.isNotEmpty ? source.hosts[0] : '',
|
||||||
|
required: true)
|
||||||
],
|
],
|
||||||
...source.searchQuerySettingFormItems.map((e) => [e])
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
if (values != null &&
|
if (values != null) {
|
||||||
(values['searchQuery'] as String?)?.isNotEmpty == true) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
importInProgress = true;
|
importInProgress = true;
|
||||||
});
|
});
|
||||||
|
if (values['url'] != source.hosts[0]) {
|
||||||
|
source = sourceProvider.getSource(values['url'],
|
||||||
|
overrideSource: source.runtimeType.toString());
|
||||||
|
}
|
||||||
var urlsWithDescriptions = await source
|
var urlsWithDescriptions = await source
|
||||||
.search(values['searchQuery'] as String, querySettings: values);
|
.search(values['searchQuery'] as String, querySettings: values);
|
||||||
if (urlsWithDescriptions.isNotEmpty) {
|
if (urlsWithDescriptions.isNotEmpty) {
|
||||||
@@ -206,17 +225,19 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
await showDialog<List<String>?>(
|
await showDialog<List<String>?>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext ctx) {
|
builder: (BuildContext ctx) {
|
||||||
return UrlSelectionModal(
|
return SelectionModal(
|
||||||
urlsWithDescriptions: urlsWithDescriptions,
|
entries: urlsWithDescriptions,
|
||||||
selectedByDefault: false,
|
selectedByDefault: false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
if (selectedUrls != null && selectedUrls.isNotEmpty) {
|
if (selectedUrls != null && selectedUrls.isNotEmpty) {
|
||||||
var errors = await appsProvider.addAppsByURL(selectedUrls);
|
var errors = await appsProvider.addAppsByURL(selectedUrls,
|
||||||
|
sourceOverride: source);
|
||||||
if (errors.isEmpty) {
|
if (errors.isEmpty) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
showError(
|
showMessage(
|
||||||
tr('importedX', args: [plural('app', selectedUrls.length)]),
|
tr('importedX',
|
||||||
|
args: [plural('apps', selectedUrls.length)]),
|
||||||
context);
|
context);
|
||||||
} else {
|
} else {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
@@ -265,15 +286,14 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
await showDialog<List<String>?>(
|
await showDialog<List<String>?>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext ctx) {
|
builder: (BuildContext ctx) {
|
||||||
return UrlSelectionModal(
|
return SelectionModal(entries: urlsWithDescriptions);
|
||||||
urlsWithDescriptions: urlsWithDescriptions);
|
|
||||||
});
|
});
|
||||||
if (selectedUrls != null) {
|
if (selectedUrls != null) {
|
||||||
var errors = await appsProvider.addAppsByURL(selectedUrls);
|
var errors = await appsProvider.addAppsByURL(selectedUrls);
|
||||||
if (errors.isEmpty) {
|
if (errors.isEmpty) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
showError(
|
showMessage(
|
||||||
tr('importedX', args: [plural('app', selectedUrls.length)]),
|
tr('importedX', args: [plural('apps', selectedUrls.length)]),
|
||||||
context);
|
context);
|
||||||
} else {
|
} else {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
@@ -296,6 +316,11 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sourceStrings = <String, List<String>>{};
|
||||||
|
sourceProvider.sources.where((e) => e.canSearch).forEach((s) {
|
||||||
|
sourceStrings[s.name] = [s.name];
|
||||||
|
});
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
body: CustomScrollView(slivers: <Widget>[
|
body: CustomScrollView(slivers: <Widget>[
|
||||||
@@ -320,21 +345,41 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
onPressed: appsProvider.apps.isEmpty ||
|
onPressed: appsProvider.apps.isEmpty ||
|
||||||
importInProgress
|
importInProgress
|
||||||
? null
|
? null
|
||||||
: runObtainiumExport,
|
: () {
|
||||||
child: Text(tr(snapshot.data != null
|
runObtainiumExport(pickOnly: true);
|
||||||
? 'obtainiumExport'
|
},
|
||||||
: 'pickExportDir')),
|
child: Text(tr('pickExportDir'),
|
||||||
|
textAlign: TextAlign.center),
|
||||||
)),
|
)),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
),
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TextButton(
|
||||||
|
style: outlineButtonStyle,
|
||||||
|
onPressed: appsProvider.apps.isEmpty ||
|
||||||
|
importInProgress ||
|
||||||
|
snapshot.data == null
|
||||||
|
? null
|
||||||
|
: runObtainiumExport,
|
||||||
|
child: Text(tr('obtainiumExport'),
|
||||||
|
textAlign: TextAlign.center),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
style: outlineButtonStyle,
|
style: outlineButtonStyle,
|
||||||
onPressed: importInProgress
|
onPressed: importInProgress
|
||||||
? null
|
? null
|
||||||
: runObtainiumImport,
|
: runObtainiumImport,
|
||||||
child: Text(tr('obtainiumImport'))))
|
child: Text(tr('obtainiumImport'),
|
||||||
|
textAlign: TextAlign.center))),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (snapshot.data != null)
|
if (snapshot.data != null)
|
||||||
@@ -350,6 +395,14 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
defaultValue: settingsProvider
|
defaultValue: settingsProvider
|
||||||
.autoExportOnChanges,
|
.autoExportOnChanges,
|
||||||
)
|
)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch(
|
||||||
|
'exportSettings',
|
||||||
|
label: tr('includeSettings'),
|
||||||
|
defaultValue: settingsProvider
|
||||||
|
.exportSettings,
|
||||||
|
)
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
onValueChanges:
|
onValueChanges:
|
||||||
@@ -362,6 +415,12 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
'autoExportOnChanges'] ==
|
'autoExportOnChanges'] ==
|
||||||
true;
|
true;
|
||||||
}
|
}
|
||||||
|
if (value['exportSettings'] !=
|
||||||
|
null) {
|
||||||
|
settingsProvider.exportSettings =
|
||||||
|
value['exportSettings'] ==
|
||||||
|
true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@@ -388,6 +447,54 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
const Divider(
|
const Divider(
|
||||||
height: 32,
|
height: 32,
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: importInProgress
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
var searchSourceName =
|
||||||
|
await showDialog<
|
||||||
|
List<String>?>(
|
||||||
|
context: context,
|
||||||
|
builder:
|
||||||
|
(BuildContext
|
||||||
|
ctx) {
|
||||||
|
return SelectionModal(
|
||||||
|
title: tr(
|
||||||
|
'selectX',
|
||||||
|
args: [
|
||||||
|
tr('source')
|
||||||
|
]),
|
||||||
|
entries:
|
||||||
|
sourceStrings,
|
||||||
|
selectedByDefault:
|
||||||
|
false,
|
||||||
|
onlyOneSelectionAllowed:
|
||||||
|
true,
|
||||||
|
titlesAreLinks:
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}) ??
|
||||||
|
[];
|
||||||
|
var searchSource =
|
||||||
|
sourceProvider.sources
|
||||||
|
.where((e) =>
|
||||||
|
searchSourceName
|
||||||
|
.contains(
|
||||||
|
e.name))
|
||||||
|
.toList();
|
||||||
|
if (searchSource.isNotEmpty) {
|
||||||
|
runSourceSearch(
|
||||||
|
searchSource[0]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(tr('searchX',
|
||||||
|
args: [tr('source')])))),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed:
|
onPressed:
|
||||||
importInProgress ? null : urlListImport,
|
importInProgress ? null : urlListImport,
|
||||||
@@ -403,39 +510,19 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
...sourceProvider.sources
|
...sourceProvider.massUrlSources.map((source) => Column(
|
||||||
.where((element) => element.canSearch)
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
.map((source) => Column(
|
children: [
|
||||||
crossAxisAlignment:
|
const SizedBox(height: 8),
|
||||||
CrossAxisAlignment.stretch,
|
TextButton(
|
||||||
children: [
|
onPressed: importInProgress
|
||||||
const SizedBox(height: 8),
|
? null
|
||||||
TextButton(
|
: () {
|
||||||
onPressed: importInProgress
|
runMassSourceImport(source);
|
||||||
? null
|
},
|
||||||
: () {
|
child: Text(
|
||||||
runSourceSearch(source);
|
tr('importX', args: [source.name])))
|
||||||
},
|
])),
|
||||||
child: Text(
|
|
||||||
tr('searchX', args: [source.name])))
|
|
||||||
]))
|
|
||||||
.toList(),
|
|
||||||
...sourceProvider.massUrlSources
|
|
||||||
.map((source) => Column(
|
|
||||||
crossAxisAlignment:
|
|
||||||
CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
TextButton(
|
|
||||||
onPressed: importInProgress
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
runMassSourceImport(source);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
tr('importX', args: [source.name])))
|
|
||||||
]))
|
|
||||||
.toList(),
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
const Divider(
|
const Divider(
|
||||||
height: 32,
|
height: 32,
|
||||||
@@ -497,126 +584,191 @@ class _ImportErrorDialogState extends State<ImportErrorDialog> {
|
|||||||
style: const TextStyle(fontStyle: FontStyle.italic),
|
style: const TextStyle(fontStyle: FontStyle.italic),
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
}).toList()
|
})
|
||||||
]),
|
]),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop(null);
|
Navigator.of(context).pop(null);
|
||||||
},
|
},
|
||||||
child: Text(tr('okay')))
|
child: Text(tr('ok')))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
class UrlSelectionModal extends StatefulWidget {
|
class SelectionModal extends StatefulWidget {
|
||||||
UrlSelectionModal(
|
SelectionModal(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.urlsWithDescriptions,
|
required this.entries,
|
||||||
this.selectedByDefault = true,
|
this.selectedByDefault = true,
|
||||||
this.onlyOneSelectionAllowed = false});
|
this.onlyOneSelectionAllowed = false,
|
||||||
|
this.titlesAreLinks = true,
|
||||||
|
this.title,
|
||||||
|
this.deselectThese = const []});
|
||||||
|
|
||||||
Map<String, List<String>> urlsWithDescriptions;
|
String? title;
|
||||||
|
Map<String, List<String>> entries;
|
||||||
bool selectedByDefault;
|
bool selectedByDefault;
|
||||||
|
List<String> deselectThese;
|
||||||
bool onlyOneSelectionAllowed;
|
bool onlyOneSelectionAllowed;
|
||||||
|
bool titlesAreLinks;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<UrlSelectionModal> createState() => _UrlSelectionModalState();
|
State<SelectionModal> createState() => _SelectionModalState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _UrlSelectionModalState extends State<UrlSelectionModal> {
|
class _SelectionModalState extends State<SelectionModal> {
|
||||||
Map<MapEntry<String, List<String>>, bool> urlWithDescriptionSelections = {};
|
Map<MapEntry<String, List<String>>, bool> entrySelections = {};
|
||||||
|
String filterRegex = '';
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
for (var url in widget.urlsWithDescriptions.entries) {
|
for (var entry in widget.entries.entries) {
|
||||||
urlWithDescriptionSelections.putIfAbsent(url,
|
entrySelections.putIfAbsent(
|
||||||
() => widget.selectedByDefault && !widget.onlyOneSelectionAllowed);
|
entry,
|
||||||
|
() =>
|
||||||
|
widget.selectedByDefault &&
|
||||||
|
!widget.onlyOneSelectionAllowed &&
|
||||||
|
!widget.deselectThese.contains(entry.key));
|
||||||
}
|
}
|
||||||
if (widget.selectedByDefault && widget.onlyOneSelectionAllowed) {
|
if (widget.selectedByDefault && widget.onlyOneSelectionAllowed) {
|
||||||
selectOnlyOne(widget.urlsWithDescriptions.entries.first.key);
|
selectOnlyOne(widget.entries.entries.first.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectOnlyOne(String url) {
|
selectOnlyOne(String url) {
|
||||||
for (var uwd in urlWithDescriptionSelections.keys) {
|
for (var e in entrySelections.keys) {
|
||||||
urlWithDescriptionSelections[uwd] = uwd.key == url;
|
entrySelections[e] = e.key == url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
Map<MapEntry<String, List<String>>, bool> filteredEntrySelections = {};
|
||||||
|
entrySelections.forEach((key, value) {
|
||||||
|
var searchableText = key.value.isEmpty ? key.key : key.value[0];
|
||||||
|
if (filterRegex.isEmpty || RegExp(filterRegex).hasMatch(searchableText)) {
|
||||||
|
filteredEntrySelections.putIfAbsent(key, () => value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (filterRegex.isNotEmpty && filteredEntrySelections.isEmpty) {
|
||||||
|
entrySelections.forEach((key, value) {
|
||||||
|
var searchableText = key.value.isEmpty ? key.key : key.value[0];
|
||||||
|
if (filterRegex.isEmpty ||
|
||||||
|
RegExp(filterRegex, caseSensitive: false)
|
||||||
|
.hasMatch(searchableText)) {
|
||||||
|
filteredEntrySelections.putIfAbsent(key, () => value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
scrollable: true,
|
scrollable: true,
|
||||||
title: Text(
|
title: Text(widget.title ?? tr('pick')),
|
||||||
widget.onlyOneSelectionAllowed ? tr('selectURL') : tr('selectURLs')),
|
|
||||||
content: Column(children: [
|
content: Column(children: [
|
||||||
...urlWithDescriptionSelections.keys.map((urlWithD) {
|
GeneratedForm(
|
||||||
|
items: [
|
||||||
|
[
|
||||||
|
GeneratedFormTextField('filter',
|
||||||
|
label: tr('filter'),
|
||||||
|
required: false,
|
||||||
|
additionalValidators: [
|
||||||
|
(value) {
|
||||||
|
return regExValidator(value);
|
||||||
|
}
|
||||||
|
])
|
||||||
|
]
|
||||||
|
],
|
||||||
|
onValueChanges: (value, valid, isBuilding) {
|
||||||
|
if (valid && !isBuilding) {
|
||||||
|
if (value['filter'] != null) {
|
||||||
|
setState(() {
|
||||||
|
filterRegex = value['filter'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
...filteredEntrySelections.keys.map((entry) {
|
||||||
selectThis(bool? value) {
|
selectThis(bool? value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
value ??= false;
|
value ??= false;
|
||||||
if (value! && widget.onlyOneSelectionAllowed) {
|
if (value! && widget.onlyOneSelectionAllowed) {
|
||||||
selectOnlyOne(urlWithD.key);
|
selectOnlyOne(entry.key);
|
||||||
} else {
|
} else {
|
||||||
urlWithDescriptionSelections[urlWithD] = value!;
|
entrySelections[entry] = value!;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var urlLink = GestureDetector(
|
var urlLink = GestureDetector(
|
||||||
onTap: () {
|
onTap: !widget.titlesAreLinks
|
||||||
launchUrlString(urlWithD.key,
|
? null
|
||||||
mode: LaunchMode.externalApplication);
|
: () {
|
||||||
},
|
launchUrlString(entry.key,
|
||||||
|
mode: LaunchMode.externalApplication);
|
||||||
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
urlWithD.value[0],
|
entry.value.isEmpty ? entry.key : entry.value[0],
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
decoration: TextDecoration.underline,
|
decoration: widget.titlesAreLinks
|
||||||
|
? TextDecoration.underline
|
||||||
|
: null,
|
||||||
fontWeight: FontWeight.bold),
|
fontWeight: FontWeight.bold),
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
),
|
),
|
||||||
Text(
|
if (widget.titlesAreLinks)
|
||||||
Uri.parse(urlWithD.key).host,
|
Text(
|
||||||
style: const TextStyle(
|
Uri.parse(entry.key).host,
|
||||||
decoration: TextDecoration.underline, fontSize: 12),
|
style: const TextStyle(
|
||||||
)
|
decoration: TextDecoration.underline, fontSize: 12),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
|
||||||
var descriptionText = Text(
|
var descriptionText = entry.value.length <= 1
|
||||||
urlWithD.value[1].length > 128
|
? const SizedBox.shrink()
|
||||||
? '${urlWithD.value[1].substring(0, 128)}...'
|
: Text(
|
||||||
: urlWithD.value[1],
|
entry.value[1].length > 128
|
||||||
style: const TextStyle(fontStyle: FontStyle.italic, fontSize: 12),
|
? '${entry.value[1].substring(0, 128)}...'
|
||||||
);
|
: entry.value[1],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontStyle: FontStyle.italic, fontSize: 12),
|
||||||
|
);
|
||||||
|
|
||||||
var selectedUrlsWithDs = urlWithDescriptionSelections.entries
|
var selectedEntries =
|
||||||
.where((e) => e.value)
|
entrySelections.entries.where((e) => e.value).toList();
|
||||||
.toList();
|
|
||||||
|
|
||||||
var singleSelectTile = ListTile(
|
var singleSelectTile = ListTile(
|
||||||
title: urlLink,
|
title: GestureDetector(
|
||||||
subtitle: GestureDetector(
|
onTap: widget.titlesAreLinks
|
||||||
onTap: () {
|
|
||||||
setState(() {
|
|
||||||
selectOnlyOne(urlWithD.key);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: descriptionText,
|
|
||||||
),
|
|
||||||
leading: Radio<String>(
|
|
||||||
value: urlWithD.key,
|
|
||||||
groupValue: selectedUrlsWithDs.isEmpty
|
|
||||||
? null
|
? null
|
||||||
: selectedUrlsWithDs.first.key.key,
|
: () {
|
||||||
|
selectThis(!(entrySelections[entry] ?? false));
|
||||||
|
},
|
||||||
|
child: urlLink,
|
||||||
|
),
|
||||||
|
subtitle: entry.value.length <= 1
|
||||||
|
? null
|
||||||
|
: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
selectOnlyOne(entry.key);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: descriptionText,
|
||||||
|
),
|
||||||
|
leading: Radio<String>(
|
||||||
|
value: entry.key,
|
||||||
|
groupValue: selectedEntries.isEmpty
|
||||||
|
? null
|
||||||
|
: selectedEntries.first.key.key,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
selectOnlyOne(urlWithD.key);
|
selectOnlyOne(entry.key);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -624,7 +776,7 @@ class _UrlSelectionModalState extends State<UrlSelectionModal> {
|
|||||||
|
|
||||||
var multiSelectTile = Row(children: [
|
var multiSelectTile = Row(children: [
|
||||||
Checkbox(
|
Checkbox(
|
||||||
value: urlWithDescriptionSelections[urlWithD],
|
value: entrySelections[entry],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
selectThis(value);
|
selectThis(value);
|
||||||
}),
|
}),
|
||||||
@@ -639,14 +791,22 @@ class _UrlSelectionModalState extends State<UrlSelectionModal> {
|
|||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8,
|
height: 8,
|
||||||
),
|
),
|
||||||
urlLink,
|
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: widget.titlesAreLinks
|
||||||
selectThis(
|
? null
|
||||||
!(urlWithDescriptionSelections[urlWithD] ?? false));
|
: () {
|
||||||
},
|
selectThis(!(entrySelections[entry] ?? false));
|
||||||
child: descriptionText,
|
},
|
||||||
|
child: urlLink,
|
||||||
),
|
),
|
||||||
|
entry.value.length <= 1
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
selectThis(!(entrySelections[entry] ?? false));
|
||||||
|
},
|
||||||
|
child: descriptionText,
|
||||||
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8,
|
height: 8,
|
||||||
)
|
)
|
||||||
@@ -666,24 +826,18 @@ class _UrlSelectionModalState extends State<UrlSelectionModal> {
|
|||||||
},
|
},
|
||||||
child: Text(tr('cancel'))),
|
child: Text(tr('cancel'))),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed:
|
onPressed: entrySelections.values.where((b) => b).isEmpty
|
||||||
urlWithDescriptionSelections.values.where((b) => b).isEmpty
|
? null
|
||||||
? null
|
: () {
|
||||||
: () {
|
Navigator.of(context).pop(entrySelections.entries
|
||||||
Navigator.of(context).pop(urlWithDescriptionSelections
|
.where((entry) => entry.value)
|
||||||
.entries
|
.map((e) => e.key.key)
|
||||||
.where((entry) => entry.value)
|
.toList());
|
||||||
.map((e) => e.key.key)
|
},
|
||||||
.toList());
|
|
||||||
},
|
|
||||||
child: Text(widget.onlyOneSelectionAllowed
|
child: Text(widget.onlyOneSelectionAllowed
|
||||||
? tr('pick')
|
? tr('pick')
|
||||||
: tr('importX', args: [
|
: tr('selectX', args: [
|
||||||
plural(
|
entrySelections.values.where((b) => b).length.toString()
|
||||||
'url',
|
|
||||||
urlWithDescriptionSelections.values
|
|
||||||
.where((b) => b)
|
|
||||||
.length)
|
|
||||||
])))
|
])))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
|
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -8,6 +7,7 @@ import 'package:obtainium/custom_errors.dart';
|
|||||||
import 'package:obtainium/main.dart';
|
import 'package:obtainium/main.dart';
|
||||||
import 'package:obtainium/providers/apps_provider.dart';
|
import 'package:obtainium/providers/apps_provider.dart';
|
||||||
import 'package:obtainium/providers/logs_provider.dart';
|
import 'package:obtainium/providers/logs_provider.dart';
|
||||||
|
import 'package:obtainium/providers/native_provider.dart';
|
||||||
import 'package:obtainium/providers/settings_provider.dart';
|
import 'package:obtainium/providers/settings_provider.dart';
|
||||||
import 'package:obtainium/providers/source_provider.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@@ -30,6 +30,29 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
settingsProvider.initializeSettings();
|
settingsProvider.initializeSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var installMethodDropdown = DropdownButtonFormField(
|
||||||
|
decoration: InputDecoration(labelText: tr('installMethod')),
|
||||||
|
value: settingsProvider.installMethod,
|
||||||
|
items: [
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: InstallMethodSettings.normal,
|
||||||
|
child: Text(tr('normal')),
|
||||||
|
),
|
||||||
|
const DropdownMenuItem(
|
||||||
|
value: InstallMethodSettings.shizuku,
|
||||||
|
child: Text('Shizuku'),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: InstallMethodSettings.root,
|
||||||
|
child: Text(tr('root')),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
settingsProvider.installMethod = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var themeDropdown = DropdownButtonFormField(
|
var themeDropdown = DropdownButtonFormField(
|
||||||
decoration: InputDecoration(labelText: tr('theme')),
|
decoration: InputDecoration(labelText: tr('theme')),
|
||||||
value: settingsProvider.theme,
|
value: settingsProvider.theme,
|
||||||
@@ -56,14 +79,14 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
var colourDropdown = DropdownButtonFormField(
|
var colourDropdown = DropdownButtonFormField(
|
||||||
decoration: InputDecoration(labelText: tr('colour')),
|
decoration: InputDecoration(labelText: tr('colour')),
|
||||||
value: settingsProvider.colour,
|
value: settingsProvider.colour,
|
||||||
items: [
|
items: const [
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: ColourSettings.basic,
|
value: ColourSettings.basic,
|
||||||
child: Text(tr('obtainium')),
|
child: Text('Obtainium'),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: ColourSettings.materialYou,
|
value: ColourSettings.materialYou,
|
||||||
child: Text(tr('materialYou')),
|
child: Text('Material You'),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@@ -310,6 +333,37 @@ 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;
|
||||||
|
})
|
||||||
|
],
|
||||||
|
),
|
||||||
|
height16,
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Flexible(child: Text(tr('parallelDownloads'))),
|
||||||
|
Switch(
|
||||||
|
value: settingsProvider.parallelDownloads,
|
||||||
|
onChanged: (value) {
|
||||||
|
settingsProvider.parallelDownloads =
|
||||||
|
value;
|
||||||
|
})
|
||||||
|
],
|
||||||
|
),
|
||||||
|
installMethodDropdown,
|
||||||
height32,
|
height32,
|
||||||
Text(
|
Text(
|
||||||
tr('sourceSpecific'),
|
tr('sourceSpecific'),
|
||||||
@@ -354,6 +408,34 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
height16,
|
height16,
|
||||||
localeDropdown,
|
localeDropdown,
|
||||||
height16,
|
height16,
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Flexible(child: Text(tr('useSystemFont'))),
|
||||||
|
Switch(
|
||||||
|
value: settingsProvider.useSystemFont,
|
||||||
|
onChanged: (useSystemFont) {
|
||||||
|
if (useSystemFont) {
|
||||||
|
NativeFeatures.loadSystemFont()
|
||||||
|
.then((fontLoadRes) {
|
||||||
|
if (fontLoadRes == 'ok') {
|
||||||
|
settingsProvider.useSystemFont =
|
||||||
|
true;
|
||||||
|
} else {
|
||||||
|
showError(
|
||||||
|
ObtainiumError(tr(
|
||||||
|
'systemFontError',
|
||||||
|
args: [fontLoadRes])),
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
settingsProvider.useSystemFont = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
),
|
||||||
|
height16,
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@@ -535,7 +617,8 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.read<LogsProvider>().get().then((logs) {
|
context.read<LogsProvider>().get().then((logs) {
|
||||||
if (logs.isEmpty) {
|
if (logs.isEmpty) {
|
||||||
showError(ObtainiumError(tr('noLogs')), context);
|
showMessage(
|
||||||
|
ObtainiumError(tr('noLogs')), context);
|
||||||
} else {
|
} else {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -549,41 +632,9 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
label: Text(tr('appLogs'))),
|
label: Text(tr('appLogs'))),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Divider(
|
const SizedBox(
|
||||||
height: 32,
|
height: 16,
|
||||||
),
|
)
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
|
||||||
child: Column(children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Flexible(child: Text(tr('debugMenu'))),
|
|
||||||
Switch(
|
|
||||||
value: settingsProvider.showDebugOpts,
|
|
||||||
onChanged: (value) {
|
|
||||||
settingsProvider.showDebugOpts = value;
|
|
||||||
})
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (settingsProvider.showDebugOpts)
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
height16,
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
AndroidAlarmManager.oneShot(
|
|
||||||
const Duration(seconds: 0),
|
|
||||||
bgUpdateCheckAlarmId + 200,
|
|
||||||
bgUpdateCheck);
|
|
||||||
showError(tr('bgTaskStarted'), context);
|
|
||||||
},
|
|
||||||
child: Text(tr('runBgCheckNow')))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
75
lib/providers/native_provider.dart
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class NativeFeatures {
|
||||||
|
static const MethodChannel _channel = MethodChannel('native');
|
||||||
|
static bool _systemFontLoaded = false;
|
||||||
|
static bool _callbacksApplied = false;
|
||||||
|
static int _resPermShizuku = -2; // not set
|
||||||
|
|
||||||
|
static Future<ByteData> _readFileBytes(String path) async {
|
||||||
|
var file = File(path);
|
||||||
|
var bytes = await file.readAsBytes();
|
||||||
|
return ByteData.view(bytes.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future _handleCalls(MethodCall call) async {
|
||||||
|
if (call.method == 'resPermShizuku') {
|
||||||
|
_resPermShizuku = call.arguments['res'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future _waitWhile(bool Function() test,
|
||||||
|
[Duration pollInterval = const Duration(milliseconds: 250)]) {
|
||||||
|
var completer = Completer();
|
||||||
|
check() {
|
||||||
|
if (test()) {
|
||||||
|
Timer(pollInterval, check);
|
||||||
|
} else {
|
||||||
|
completer.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check();
|
||||||
|
return completer.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<String> loadSystemFont() async {
|
||||||
|
if (_systemFontLoaded) { return "ok"; }
|
||||||
|
var getFontRes = await _channel.invokeMethod('getSystemFont');
|
||||||
|
if (getFontRes[0] != '/') { return getFontRes; } // Error
|
||||||
|
var fontLoader = FontLoader('SystemFont');
|
||||||
|
fontLoader.addFont(_readFileBytes(getFontRes));
|
||||||
|
await fontLoader.load();
|
||||||
|
_systemFontLoaded = true;
|
||||||
|
return "ok";
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<int> checkPermissionShizuku() async {
|
||||||
|
if (!_callbacksApplied) {
|
||||||
|
_channel.setMethodCallHandler(_handleCalls);
|
||||||
|
_callbacksApplied = true;
|
||||||
|
}
|
||||||
|
int res = await _channel.invokeMethod('checkPermissionShizuku');
|
||||||
|
if (res == -2) {
|
||||||
|
await _waitWhile(() => _resPermShizuku == -2);
|
||||||
|
res = _resPermShizuku;
|
||||||
|
_resPermShizuku = -2;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> checkPermissionRoot() async {
|
||||||
|
return await _channel.invokeMethod('checkPermissionRoot');
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> installWithShizuku({required String apkFileUri}) async {
|
||||||
|
return await _channel.invokeMethod(
|
||||||
|
'installWithShizuku', {'apkFileUri': apkFileUri});
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> installWithRoot({required String apkFilePath}) async {
|
||||||
|
return await _channel.invokeMethod(
|
||||||
|
'installWithRoot', {'apkFilePath': apkFilePath});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ class UpdateNotification extends ObtainiumNotification {
|
|||||||
tr('updatesAvailable'),
|
tr('updatesAvailable'),
|
||||||
'',
|
'',
|
||||||
'UPDATES_AVAILABLE',
|
'UPDATES_AVAILABLE',
|
||||||
tr('updatesAvailable'),
|
tr('updatesAvailableNotifChannel'),
|
||||||
tr('updatesAvailableNotifDescription'),
|
tr('updatesAvailableNotifDescription'),
|
||||||
Importance.max) {
|
Importance.max) {
|
||||||
message = updates.isEmpty
|
message = updates.isEmpty
|
||||||
@@ -42,8 +42,14 @@ class UpdateNotification extends ObtainiumNotification {
|
|||||||
|
|
||||||
class SilentUpdateNotification extends ObtainiumNotification {
|
class SilentUpdateNotification extends ObtainiumNotification {
|
||||||
SilentUpdateNotification(List<App> updates, {int? id})
|
SilentUpdateNotification(List<App> updates, {int? id})
|
||||||
: super(id ?? 3, tr('appsUpdated'), '', 'APPS_UPDATED', tr('appsUpdated'),
|
: super(
|
||||||
tr('appsUpdatedNotifDescription'), Importance.defaultImportance) {
|
id ?? 3,
|
||||||
|
tr('appsUpdated'),
|
||||||
|
'',
|
||||||
|
'APPS_UPDATED',
|
||||||
|
tr('appsUpdatedNotifChannel'),
|
||||||
|
tr('appsUpdatedNotifDescription'),
|
||||||
|
Importance.defaultImportance) {
|
||||||
message = updates.length == 1
|
message = updates.length == 1
|
||||||
? tr('xWasUpdatedToY',
|
? tr('xWasUpdatedToY',
|
||||||
args: [updates[0].finalName, updates[0].latestVersion])
|
args: [updates[0].finalName, updates[0].latestVersion])
|
||||||
@@ -59,7 +65,7 @@ class SilentUpdateAttemptNotification extends ObtainiumNotification {
|
|||||||
tr('appsPossiblyUpdated'),
|
tr('appsPossiblyUpdated'),
|
||||||
'',
|
'',
|
||||||
'APPS_POSSIBLY_UPDATED',
|
'APPS_POSSIBLY_UPDATED',
|
||||||
tr('appsPossiblyUpdated'),
|
tr('appsPossiblyUpdatedNotifChannel'),
|
||||||
tr('appsPossiblyUpdatedNotifDescription'),
|
tr('appsPossiblyUpdatedNotifDescription'),
|
||||||
Importance.defaultImportance) {
|
Importance.defaultImportance) {
|
||||||
message = updates.length == 1
|
message = updates.length == 1
|
||||||
@@ -77,15 +83,21 @@ class ErrorCheckingUpdatesNotification extends ObtainiumNotification {
|
|||||||
tr('errorCheckingUpdates'),
|
tr('errorCheckingUpdates'),
|
||||||
error,
|
error,
|
||||||
'BG_UPDATE_CHECK_ERROR',
|
'BG_UPDATE_CHECK_ERROR',
|
||||||
tr('errorCheckingUpdates'),
|
tr('errorCheckingUpdatesNotifChannel'),
|
||||||
tr('errorCheckingUpdatesNotifDescription'),
|
tr('errorCheckingUpdatesNotifDescription'),
|
||||||
Importance.high);
|
Importance.high);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppsRemovedNotification extends ObtainiumNotification {
|
class AppsRemovedNotification extends ObtainiumNotification {
|
||||||
AppsRemovedNotification(List<List<String>> namedReasons)
|
AppsRemovedNotification(List<List<String>> namedReasons)
|
||||||
: super(6, tr('appsRemoved'), '', 'APPS_REMOVED', tr('appsRemoved'),
|
: super(
|
||||||
tr('appsRemovedNotifDescription'), Importance.max) {
|
6,
|
||||||
|
tr('appsRemoved'),
|
||||||
|
'',
|
||||||
|
'APPS_REMOVED',
|
||||||
|
tr('appsRemovedNotifChannel'),
|
||||||
|
tr('appsRemovedNotifDescription'),
|
||||||
|
Importance.max) {
|
||||||
message = '';
|
message = '';
|
||||||
for (var r in namedReasons) {
|
for (var r in namedReasons) {
|
||||||
message += '${tr('xWasRemovedDueToErrorY', args: [r[0], r[1]])} \n';
|
message += '${tr('xWasRemovedDueToErrorY', args: [r[0], r[1]])} \n';
|
||||||
@@ -101,7 +113,7 @@ class DownloadNotification extends ObtainiumNotification {
|
|||||||
tr('downloadingX', args: [appName]),
|
tr('downloadingX', args: [appName]),
|
||||||
'',
|
'',
|
||||||
'APP_DOWNLOADING',
|
'APP_DOWNLOADING',
|
||||||
tr('downloadingX', args: [tr('app')]),
|
tr('downloadingXNotifChannel', args: [tr('app')]),
|
||||||
tr('downloadNotifDescription'),
|
tr('downloadNotifDescription'),
|
||||||
Importance.low,
|
Importance.low,
|
||||||
onlyAlertOnce: true,
|
onlyAlertOnce: true,
|
||||||
@@ -113,7 +125,7 @@ final completeInstallationNotification = ObtainiumNotification(
|
|||||||
tr('completeAppInstallation'),
|
tr('completeAppInstallation'),
|
||||||
tr('obtainiumMustBeOpenToInstallApps'),
|
tr('obtainiumMustBeOpenToInstallApps'),
|
||||||
'COMPLETE_INSTALL',
|
'COMPLETE_INSTALL',
|
||||||
tr('completeAppInstallation'),
|
tr('completeAppInstallationNotifChannel'),
|
||||||
tr('completeAppInstallationNotifDescription'),
|
tr('completeAppInstallationNotifDescription'),
|
||||||
Importance.max);
|
Importance.max);
|
||||||
|
|
||||||
@@ -124,7 +136,7 @@ class CheckingUpdatesNotification extends ObtainiumNotification {
|
|||||||
tr('checkingForUpdates'),
|
tr('checkingForUpdates'),
|
||||||
appName,
|
appName,
|
||||||
'BG_UPDATE_CHECK',
|
'BG_UPDATE_CHECK',
|
||||||
tr('checkingForUpdates'),
|
tr('checkingForUpdatesNotifChannel'),
|
||||||
tr('checkingForUpdatesNotifDescription'),
|
tr('checkingForUpdatesNotifDescription'),
|
||||||
Importance.min);
|
Importance.min);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,11 @@ import 'package:permission_handler/permission_handler.dart';
|
|||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:shared_storage/shared_storage.dart' as saf;
|
import 'package:shared_storage/shared_storage.dart' as saf;
|
||||||
|
|
||||||
String obtainiumTempId = 'imranr98_obtainium_${GitHub().host}';
|
String obtainiumTempId = 'imranr98_obtainium_${GitHub().hosts[0]}';
|
||||||
String obtainiumId = 'dev.imranr.obtainium';
|
String obtainiumId = 'dev.imranr.obtainium';
|
||||||
|
String obtainiumUrl = 'https://github.com/ImranR98/Obtainium';
|
||||||
|
|
||||||
|
enum InstallMethodSettings { normal, shizuku, root }
|
||||||
|
|
||||||
enum ThemeSettings { system, light, dark }
|
enum ThemeSettings { system, light, dark }
|
||||||
|
|
||||||
@@ -49,6 +52,25 @@ class SettingsProvider with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get useSystemFont {
|
||||||
|
return prefs?.getBool('useSystemFont') ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
set useSystemFont(bool useSystemFont) {
|
||||||
|
prefs?.setBool('useSystemFont', useSystemFont);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
InstallMethodSettings get installMethod {
|
||||||
|
return InstallMethodSettings.values[
|
||||||
|
prefs?.getInt('installMethod') ?? InstallMethodSettings.normal.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
set installMethod(InstallMethodSettings t) {
|
||||||
|
prefs?.setInt('installMethod', t.index);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
ThemeSettings get theme {
|
ThemeSettings get theme {
|
||||||
return ThemeSettings
|
return ThemeSettings
|
||||||
.values[prefs?.getInt('theme') ?? ThemeSettings.system.index];
|
.values[prefs?.getInt('theme') ?? ThemeSettings.system.index];
|
||||||
@@ -213,7 +235,8 @@ class SettingsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String? getSettingString(String settingId) {
|
String? getSettingString(String settingId) {
|
||||||
return prefs?.getString(settingId);
|
String? str = prefs?.getString(settingId);
|
||||||
|
return str?.isNotEmpty == true ? str : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSettingString(String settingId, String value) {
|
void setSettingString(String settingId, String value) {
|
||||||
@@ -332,15 +355,15 @@ class SettingsProvider with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime get lastBGCheckTime {
|
DateTime get lastCompletedBGCheckTime {
|
||||||
int? temp = prefs?.getInt('lastBGCheckTime');
|
int? temp = prefs?.getInt('lastCompletedBGCheckTime');
|
||||||
return temp != null
|
return temp != null
|
||||||
? DateTime.fromMillisecondsSinceEpoch(temp)
|
? DateTime.fromMillisecondsSinceEpoch(temp)
|
||||||
: DateTime.fromMillisecondsSinceEpoch(0);
|
: DateTime.fromMillisecondsSinceEpoch(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
set lastBGCheckTime(DateTime val) {
|
set lastCompletedBGCheckTime(DateTime val) {
|
||||||
prefs?.setInt('lastBGCheckTime', val.millisecondsSinceEpoch);
|
prefs?.setInt('lastCompletedBGCheckTime', val.millisecondsSinceEpoch);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,4 +429,40 @@ class SettingsProvider with ChangeNotifier {
|
|||||||
prefs?.setBool('autoExportOnChanges', val);
|
prefs?.setBool('autoExportOnChanges', val);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get onlyCheckInstalledOrTrackOnlyApps {
|
||||||
|
return prefs?.getBool('onlyCheckInstalledOrTrackOnlyApps') ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
set onlyCheckInstalledOrTrackOnlyApps(bool val) {
|
||||||
|
prefs?.setBool('onlyCheckInstalledOrTrackOnlyApps', val);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get exportSettings {
|
||||||
|
return prefs?.getBool('exportSettings') ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
set exportSettings(bool val) {
|
||||||
|
prefs?.setBool('exportSettings', val);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get parallelDownloads {
|
||||||
|
return prefs?.getBool('parallelDownloads') ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
set parallelDownloads(bool val) {
|
||||||
|
prefs?.setBool('parallelDownloads', val);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> get searchDeselected {
|
||||||
|
return prefs?.getStringList('searchDeselected') ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
set searchDeselected(List<String> list) {
|
||||||
|
prefs?.setStringList('searchDeselected', list);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import 'package:obtainium/app_sources/apkmirror.dart';
|
|||||||
import 'package:obtainium/app_sources/apkpure.dart';
|
import 'package:obtainium/app_sources/apkpure.dart';
|
||||||
import 'package:obtainium/app_sources/aptoide.dart';
|
import 'package:obtainium/app_sources/aptoide.dart';
|
||||||
import 'package:obtainium/app_sources/codeberg.dart';
|
import 'package:obtainium/app_sources/codeberg.dart';
|
||||||
|
import 'package:obtainium/app_sources/directAPKLink.dart';
|
||||||
import 'package:obtainium/app_sources/fdroid.dart';
|
import 'package:obtainium/app_sources/fdroid.dart';
|
||||||
import 'package:obtainium/app_sources/fdroidrepo.dart';
|
import 'package:obtainium/app_sources/fdroidrepo.dart';
|
||||||
import 'package:obtainium/app_sources/github.dart';
|
import 'package:obtainium/app_sources/github.dart';
|
||||||
@@ -19,7 +20,6 @@ import 'package:obtainium/app_sources/huaweiappgallery.dart';
|
|||||||
import 'package:obtainium/app_sources/izzyondroid.dart';
|
import 'package:obtainium/app_sources/izzyondroid.dart';
|
||||||
import 'package:obtainium/app_sources/html.dart';
|
import 'package:obtainium/app_sources/html.dart';
|
||||||
import 'package:obtainium/app_sources/jenkins.dart';
|
import 'package:obtainium/app_sources/jenkins.dart';
|
||||||
import 'package:obtainium/app_sources/mullvad.dart';
|
|
||||||
import 'package:obtainium/app_sources/neutroncode.dart';
|
import 'package:obtainium/app_sources/neutroncode.dart';
|
||||||
import 'package:obtainium/app_sources/signal.dart';
|
import 'package:obtainium/app_sources/signal.dart';
|
||||||
import 'package:obtainium/app_sources/sourceforge.dart';
|
import 'package:obtainium/app_sources/sourceforge.dart';
|
||||||
@@ -28,6 +28,7 @@ import 'package:obtainium/app_sources/steammobile.dart';
|
|||||||
import 'package:obtainium/app_sources/telegramapp.dart';
|
import 'package:obtainium/app_sources/telegramapp.dart';
|
||||||
import 'package:obtainium/app_sources/uptodown.dart';
|
import 'package:obtainium/app_sources/uptodown.dart';
|
||||||
import 'package:obtainium/app_sources/vlc.dart';
|
import 'package:obtainium/app_sources/vlc.dart';
|
||||||
|
import 'package:obtainium/app_sources/whatsapp.dart';
|
||||||
import 'package:obtainium/components/generated_form.dart';
|
import 'package:obtainium/components/generated_form.dart';
|
||||||
import 'package:obtainium/custom_errors.dart';
|
import 'package:obtainium/custom_errors.dart';
|
||||||
import 'package:obtainium/mass_app_sources/githubstars.dart';
|
import 'package:obtainium/mass_app_sources/githubstars.dart';
|
||||||
@@ -66,10 +67,11 @@ appJSONCompatibilityModifiers(Map<String, dynamic> json) {
|
|||||||
.reduce((value, element) => [...value, ...element]);
|
.reduce((value, element) => [...value, ...element]);
|
||||||
Map<String, dynamic> additionalSettings =
|
Map<String, dynamic> additionalSettings =
|
||||||
getDefaultValuesFromFormItems([formItems]);
|
getDefaultValuesFromFormItems([formItems]);
|
||||||
|
Map<String, dynamic> originalAdditionalSettings = {};
|
||||||
if (json['additionalSettings'] != null) {
|
if (json['additionalSettings'] != null) {
|
||||||
additionalSettings.addEntries(
|
originalAdditionalSettings =
|
||||||
Map<String, dynamic>.from(jsonDecode(json['additionalSettings']))
|
Map<String, dynamic>.from(jsonDecode(json['additionalSettings']));
|
||||||
.entries);
|
additionalSettings.addEntries(originalAdditionalSettings.entries);
|
||||||
}
|
}
|
||||||
// If needed, migrate old-style additionalData to newer-style additionalSettings (V1)
|
// If needed, migrate old-style additionalData to newer-style additionalSettings (V1)
|
||||||
if (json['additionalData'] != null) {
|
if (json['additionalData'] != null) {
|
||||||
@@ -102,6 +104,21 @@ appJSONCompatibilityModifiers(Map<String, dynamic> json) {
|
|||||||
additionalSettings.remove('releaseDateAsVersion');
|
additionalSettings.remove('releaseDateAsVersion');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Convert dropdown style version detection options back into bool style
|
||||||
|
if (additionalSettings['versionDetection'] == 'standardVersionDetection') {
|
||||||
|
additionalSettings['versionDetection'] = true;
|
||||||
|
} else if (additionalSettings['versionDetection'] == 'noVersionDetection') {
|
||||||
|
additionalSettings['versionDetection'] = false;
|
||||||
|
} else if (additionalSettings['versionDetection'] == 'releaseDateAsVersion') {
|
||||||
|
additionalSettings['versionDetection'] = false;
|
||||||
|
additionalSettings['releaseDateAsVersion'] = true;
|
||||||
|
}
|
||||||
|
// Convert bool style pseudo version method to dropdown style
|
||||||
|
if (originalAdditionalSettings['supportFixedAPKURL'] == true) {
|
||||||
|
additionalSettings['defaultPseudoVersioningMethod'] = 'partialAPKHash';
|
||||||
|
} else if (originalAdditionalSettings['supportFixedAPKURL'] == false) {
|
||||||
|
additionalSettings['defaultPseudoVersioningMethod'] = 'APKLinkHash';
|
||||||
|
}
|
||||||
// Ensure additionalSettings are correctly typed
|
// Ensure additionalSettings are correctly typed
|
||||||
for (var item in formItems) {
|
for (var item in formItems) {
|
||||||
if (additionalSettings[item.key] != null) {
|
if (additionalSettings[item.key] != null) {
|
||||||
@@ -133,6 +150,53 @@ appJSONCompatibilityModifiers(Map<String, dynamic> json) {
|
|||||||
if (additionalSettings['autoApkFilterByArch'] == null) {
|
if (additionalSettings['autoApkFilterByArch'] == null) {
|
||||||
additionalSettings['autoApkFilterByArch'] = false;
|
additionalSettings['autoApkFilterByArch'] = false;
|
||||||
}
|
}
|
||||||
|
if (source.runtimeType == HTML().runtimeType) {
|
||||||
|
// HTML 'fixed URL' support should be disabled if it previously did not exist
|
||||||
|
if (originalAdditionalSettings['supportFixedAPKURL'] == null) {
|
||||||
|
additionalSettings['supportFixedAPKURL'] = false;
|
||||||
|
}
|
||||||
|
// HTML key rename
|
||||||
|
if (originalAdditionalSettings['sortByFileNamesNotLinks'] != null) {
|
||||||
|
additionalSettings['sortByLastLinkSegment'] =
|
||||||
|
originalAdditionalSettings['sortByFileNamesNotLinks'];
|
||||||
|
}
|
||||||
|
// HTML single 'intermediate link' should be converted to multi-support version
|
||||||
|
if (originalAdditionalSettings['intermediateLinkRegex'] != null &&
|
||||||
|
additionalSettings['intermediateLinkRegex']?.isNotEmpty != true) {
|
||||||
|
additionalSettings['intermediateLink'] = [
|
||||||
|
{
|
||||||
|
'customLinkFilterRegex':
|
||||||
|
originalAdditionalSettings['intermediateLinkRegex'],
|
||||||
|
'filterByLinkText':
|
||||||
|
originalAdditionalSettings['intermediateLinkByText']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if ((additionalSettings['intermediateLink']?.length ?? 0) > 0) {
|
||||||
|
additionalSettings['intermediateLink'] =
|
||||||
|
additionalSettings['intermediateLink'].where((e) {
|
||||||
|
return e['customLinkFilterRegex']?.isNotEmpty == true;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
// Steam source apps should be converted to HTML (#1244)
|
||||||
|
var legacySteamSourceApps = SteamMobile().apks.keys;
|
||||||
|
if (legacySteamSourceApps.contains(additionalSettings['app'] ?? '')) {
|
||||||
|
json['url'] = '${json['url']}/mobile';
|
||||||
|
var replacementAdditionalSettings = getDefaultValuesFromFormItems(
|
||||||
|
HTML().combinedAppSpecificSettingFormItems);
|
||||||
|
for (var s in replacementAdditionalSettings.keys) {
|
||||||
|
if (additionalSettings.containsKey(s)) {
|
||||||
|
replacementAdditionalSettings[s] = additionalSettings[s];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
replacementAdditionalSettings['customLinkFilterRegex'] =
|
||||||
|
'/${additionalSettings['app']}-(([0-9]+\\.?){1,})\\.apk';
|
||||||
|
replacementAdditionalSettings['versionExtractionRegEx'] =
|
||||||
|
replacementAdditionalSettings['customLinkFilterRegex'];
|
||||||
|
replacementAdditionalSettings['matchGroupToUse'] = '\$1';
|
||||||
|
additionalSettings = replacementAdditionalSettings;
|
||||||
|
}
|
||||||
|
}
|
||||||
json['additionalSettings'] = jsonEncode(additionalSettings);
|
json['additionalSettings'] = jsonEncode(additionalSettings);
|
||||||
// F-Droid no longer needs cloudflare exception since override can be used - migrate apps appropriately
|
// F-Droid no longer needs cloudflare exception since override can be used - migrate apps appropriately
|
||||||
// This allows us to reverse the changes made for issue #418 (support cloudflare.f-droid)
|
// This allows us to reverse the changes made for issue #418 (support cloudflare.f-droid)
|
||||||
@@ -228,9 +292,10 @@ class App {
|
|||||||
json['installedVersion'] == null
|
json['installedVersion'] == null
|
||||||
? null
|
? null
|
||||||
: json['installedVersion'] as String,
|
: json['installedVersion'] as String,
|
||||||
json['latestVersion'] as String,
|
(json['latestVersion'] ?? tr('unknown')) as String,
|
||||||
assumed2DlistToStringMapList(jsonDecode(json['apkUrls'])),
|
assumed2DlistToStringMapList(jsonDecode(
|
||||||
json['preferredApkIndex'] as int,
|
(json['apkUrls'] ?? '[["placeholder", "placeholder"]]'))),
|
||||||
|
(json['preferredApkIndex'] ?? -1) as int,
|
||||||
jsonDecode(json['additionalSettings']) as Map<String, dynamic>,
|
jsonDecode(json['additionalSettings']) as Map<String, dynamic>,
|
||||||
json['lastUpdateCheck'] == null
|
json['lastUpdateCheck'] == null
|
||||||
? null
|
? null
|
||||||
@@ -282,9 +347,6 @@ preStandardizeUrl(String url) {
|
|||||||
url.toLowerCase().indexOf('https://') != 0) {
|
url.toLowerCase().indexOf('https://') != 0) {
|
||||||
url = 'https://$url';
|
url = 'https://$url';
|
||||||
}
|
}
|
||||||
if (url.toLowerCase().indexOf('https://www.') == 0) {
|
|
||||||
url = 'https://${url.substring(12)}';
|
|
||||||
}
|
|
||||||
url = url
|
url = url
|
||||||
.split('/')
|
.split('/')
|
||||||
.where((e) => e.isNotEmpty)
|
.where((e) => e.isNotEmpty)
|
||||||
@@ -320,36 +382,38 @@ List<MapEntry<String, String>> getApkUrlsFromUrls(List<String> urls) =>
|
|||||||
return MapEntry(apkSegs.isNotEmpty ? apkSegs.last : segments.last, e);
|
return MapEntry(apkSegs.isNotEmpty ? apkSegs.last : segments.last, e);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
|
getSourceRegex(List<String> hosts) {
|
||||||
|
return '(${hosts.join('|').replaceAll('.', '\\.')})';
|
||||||
|
}
|
||||||
|
|
||||||
abstract class AppSource {
|
abstract class AppSource {
|
||||||
String? host;
|
List<String> hosts = [];
|
||||||
bool hostChanged = false;
|
bool hostChanged = false;
|
||||||
late String name;
|
late String name;
|
||||||
bool enforceTrackOnly = false;
|
bool enforceTrackOnly = false;
|
||||||
bool changeLogIfAnyIsMarkDown = true;
|
bool changeLogIfAnyIsMarkDown = true;
|
||||||
bool appIdInferIsOptional = false;
|
bool appIdInferIsOptional = false;
|
||||||
bool allowSubDomains = false;
|
bool allowSubDomains = false;
|
||||||
|
bool naiveStandardVersionDetection = false;
|
||||||
|
bool neverAutoSelect = false;
|
||||||
|
bool showReleaseDateAsVersionToggle = false;
|
||||||
|
bool versionDetectionDisallowed = false;
|
||||||
|
List<String> excludeCommonSettingKeys = [];
|
||||||
|
|
||||||
AppSource() {
|
AppSource() {
|
||||||
name = runtimeType.toString();
|
name = runtimeType.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
overrideVersionDetectionFormDefault(String vd,
|
overrideAdditionalAppSpecificSourceAgnosticSettingSwitch(String key,
|
||||||
{bool disableStandard = false, bool disableRelDate = false}) {
|
{bool disabled = true, bool defaultValue = true}) {
|
||||||
additionalAppSpecificSourceAgnosticSettingFormItems =
|
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly =
|
||||||
additionalAppSpecificSourceAgnosticSettingFormItems.map((e) {
|
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly
|
||||||
|
.map((e) {
|
||||||
return e.map((e2) {
|
return e.map((e2) {
|
||||||
if (e2.key == 'versionDetection') {
|
if (e2.key == key) {
|
||||||
var item = e2 as GeneratedFormDropdown;
|
var item = e2 as GeneratedFormSwitch;
|
||||||
item.defaultValue = vd;
|
item.disabled = disabled;
|
||||||
item.disabledOptKeys = [];
|
item.defaultValue = defaultValue;
|
||||||
if (disableStandard) {
|
|
||||||
item.disabledOptKeys?.add('standardVersionDetection');
|
|
||||||
}
|
|
||||||
if (disableRelDate) {
|
|
||||||
item.disabledOptKeys?.add('releaseDateAsVersion');
|
|
||||||
}
|
|
||||||
item.disabledOptKeys =
|
|
||||||
item.disabledOptKeys?.where((element) => element != vd).toList();
|
|
||||||
}
|
}
|
||||||
return e2;
|
return e2;
|
||||||
}).toList();
|
}).toList();
|
||||||
@@ -365,17 +429,19 @@ abstract class AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, String>?> getRequestHeaders(
|
Future<Map<String, String>?> getRequestHeaders(
|
||||||
{Map<String, dynamic> additionalSettings = const <String, dynamic>{},
|
Map<String, dynamic> additionalSettings,
|
||||||
bool forAPKDownload = false}) async {
|
{bool forAPKDownload = false}) async {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Response> sourceRequest(String url,
|
App endOfGetAppChanges(App app) {
|
||||||
{bool followRedirects = true,
|
return app;
|
||||||
Map<String, dynamic> additionalSettings =
|
}
|
||||||
const <String, dynamic>{}}) async {
|
|
||||||
var requestHeaders =
|
Future<Response> sourceRequest(
|
||||||
await getRequestHeaders(additionalSettings: additionalSettings);
|
String url, Map<String, dynamic> additionalSettings,
|
||||||
|
{bool followRedirects = true}) async {
|
||||||
|
var requestHeaders = await getRequestHeaders(additionalSettings);
|
||||||
if (requestHeaders != null || followRedirects == false) {
|
if (requestHeaders != null || followRedirects == false) {
|
||||||
var req = Request('GET', Uri.parse(url));
|
var req = Request('GET', Uri.parse(url));
|
||||||
req.followRedirects = followRedirects;
|
req.followRedirects = followRedirects;
|
||||||
@@ -403,7 +469,7 @@ abstract class AppSource {
|
|||||||
|
|
||||||
// Some additional data may be needed for Apps regardless of Source
|
// Some additional data may be needed for Apps regardless of Source
|
||||||
List<List<GeneratedFormItem>>
|
List<List<GeneratedFormItem>>
|
||||||
additionalAppSpecificSourceAgnosticSettingFormItems = [
|
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly = [
|
||||||
[
|
[
|
||||||
GeneratedFormSwitch(
|
GeneratedFormSwitch(
|
||||||
'trackOnly',
|
'trackOnly',
|
||||||
@@ -411,16 +477,22 @@ abstract class AppSource {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
GeneratedFormDropdown(
|
GeneratedFormTextField('versionExtractionRegEx',
|
||||||
'versionDetection',
|
label: tr('versionExtractionRegEx'),
|
||||||
[
|
required: false,
|
||||||
MapEntry(
|
additionalValidators: [(value) => regExValidator(value)]),
|
||||||
'standardVersionDetection', tr('standardVersionDetection')),
|
],
|
||||||
MapEntry('releaseDateAsVersion', tr('releaseDateAsVersion')),
|
[
|
||||||
MapEntry('noVersionDetection', tr('noVersionDetection'))
|
GeneratedFormTextField('matchGroupToUse',
|
||||||
],
|
label: tr('matchGroupToUse'), required: false, hint: '\$0')
|
||||||
label: tr('versionDetection'),
|
],
|
||||||
defaultValue: 'standardVersionDetection')
|
[
|
||||||
|
GeneratedFormSwitch('versionDetection',
|
||||||
|
label: tr('versionDetectionExplanation'), defaultValue: true)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch('useVersionCodeAsOSVersion',
|
||||||
|
label: tr('useVersionCodeAsOSVersion'), defaultValue: false)
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
GeneratedFormTextField('apkFilterRegEx',
|
GeneratedFormTextField('apkFilterRegEx',
|
||||||
@@ -432,6 +504,11 @@ abstract class AppSource {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch('invertAPKFilter',
|
||||||
|
label: '${tr('invertRegEx')} (${tr('filterAPKsByRegEx')})',
|
||||||
|
defaultValue: false)
|
||||||
|
],
|
||||||
[
|
[
|
||||||
GeneratedFormSwitch('autoApkFilterByArch',
|
GeneratedFormSwitch('autoApkFilterByArch',
|
||||||
label: tr('autoApkFilterByArch'), defaultValue: true)
|
label: tr('autoApkFilterByArch'), defaultValue: true)
|
||||||
@@ -440,14 +517,58 @@ abstract class AppSource {
|
|||||||
[
|
[
|
||||||
GeneratedFormSwitch('exemptFromBackgroundUpdates',
|
GeneratedFormSwitch('exemptFromBackgroundUpdates',
|
||||||
label: tr('exemptFromBackgroundUpdates'))
|
label: tr('exemptFromBackgroundUpdates'))
|
||||||
]
|
],
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch('skipUpdateNotifications',
|
||||||
|
label: tr('skipUpdateNotifications'))
|
||||||
|
],
|
||||||
|
[GeneratedFormTextField('about', label: tr('about'), required: false)]
|
||||||
];
|
];
|
||||||
|
|
||||||
// Previous 2 variables combined into one at runtime for convenient usage
|
// Previous 2 variables combined into one at runtime for convenient usage
|
||||||
List<List<GeneratedFormItem>> get combinedAppSpecificSettingFormItems {
|
List<List<GeneratedFormItem>> get combinedAppSpecificSettingFormItems {
|
||||||
|
if (showReleaseDateAsVersionToggle == true) {
|
||||||
|
if (additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly
|
||||||
|
.indexWhere((List<GeneratedFormItem> e) =>
|
||||||
|
e.indexWhere((GeneratedFormItem i) =>
|
||||||
|
i.key == 'releaseDateAsVersion') >=
|
||||||
|
0) <
|
||||||
|
0) {
|
||||||
|
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly.insert(
|
||||||
|
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly
|
||||||
|
.indexWhere((List<GeneratedFormItem> e) =>
|
||||||
|
e.indexWhere((GeneratedFormItem i) =>
|
||||||
|
i.key == 'versionDetection') >=
|
||||||
|
0) +
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
GeneratedFormSwitch('releaseDateAsVersion',
|
||||||
|
label:
|
||||||
|
'${tr('releaseDateAsVersion')} (${tr('pseudoVersion')})',
|
||||||
|
defaultValue: false)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly =
|
||||||
|
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly
|
||||||
|
.map((e) => e
|
||||||
|
.where((ee) => !excludeCommonSettingKeys.contains(ee.key))
|
||||||
|
.toList())
|
||||||
|
.where((e) => e.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
if (versionDetectionDisallowed) {
|
||||||
|
overrideAdditionalAppSpecificSourceAgnosticSettingSwitch(
|
||||||
|
'versionDetection',
|
||||||
|
disabled: true,
|
||||||
|
defaultValue: false);
|
||||||
|
overrideAdditionalAppSpecificSourceAgnosticSettingSwitch(
|
||||||
|
'useVersionCodeAsOSVersion',
|
||||||
|
disabled: true,
|
||||||
|
defaultValue: false);
|
||||||
|
}
|
||||||
return [
|
return [
|
||||||
...additionalSourceAppSpecificSettingFormItems,
|
...additionalSourceAppSpecificSettingFormItems,
|
||||||
...additionalAppSpecificSourceAgnosticSettingFormItems
|
...additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,12 +598,13 @@ abstract class AppSource {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> apkUrlPrefetchModifier(
|
Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
|
||||||
String apkUrl, String standardUrl) async {
|
Map<String, dynamic> additionalSettings) async {
|
||||||
return apkUrl;
|
return apkUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canSearch = false;
|
bool canSearch = false;
|
||||||
|
bool excludeFromMassSearch = false;
|
||||||
List<GeneratedFormItem> searchQuerySettingFormItems = [];
|
List<GeneratedFormItem> searchQuerySettingFormItems = [];
|
||||||
Future<Map<String, List<String>>> search(String query,
|
Future<Map<String, List<String>>> search(String query,
|
||||||
{Map<String, dynamic> querySettings = const {}}) {
|
{Map<String, dynamic> querySettings = const {}}) {
|
||||||
@@ -535,6 +657,76 @@ intValidator(String? value, {bool positive = false}) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isTempId(App app) {
|
||||||
|
// return app.id == generateTempID(app.url, app.additionalSettings);
|
||||||
|
return RegExp('^[0-9]+\$').hasMatch(app.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceMatchGroupsInString(RegExpMatch match, String matchGroupString) {
|
||||||
|
if (RegExp('^\\d+\$').hasMatch(matchGroupString)) {
|
||||||
|
matchGroupString = '\$$matchGroupString';
|
||||||
|
}
|
||||||
|
// Regular expression to match numbers in the input string
|
||||||
|
final numberRegex = RegExp(r'\$\d+');
|
||||||
|
// Extract all numbers from the input string
|
||||||
|
final numbers = numberRegex.allMatches(matchGroupString);
|
||||||
|
if (numbers.isEmpty) {
|
||||||
|
// If no numbers found, return the original string
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Replace numbers with corresponding match groups
|
||||||
|
var outputString = matchGroupString;
|
||||||
|
for (final numberMatch in numbers) {
|
||||||
|
final number = numberMatch.group(0)!;
|
||||||
|
final matchGroup = match.group(int.parse(number.substring(1))) ?? '';
|
||||||
|
// Check if the number is preceded by a single backslash
|
||||||
|
final isEscaped = outputString.contains('\\$number');
|
||||||
|
// Replace the number with the corresponding match group
|
||||||
|
if (!isEscaped) {
|
||||||
|
outputString = outputString.replaceAll(number, matchGroup);
|
||||||
|
} else {
|
||||||
|
outputString = outputString.replaceAll('\\$number', number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputString;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? extractVersion(String? versionExtractionRegEx, String? matchGroupString,
|
||||||
|
String stringToCheck) {
|
||||||
|
if (versionExtractionRegEx?.isNotEmpty == true) {
|
||||||
|
String? version = stringToCheck;
|
||||||
|
var match = RegExp(versionExtractionRegEx!).allMatches(version);
|
||||||
|
if (match.isEmpty) {
|
||||||
|
throw NoVersionError();
|
||||||
|
}
|
||||||
|
matchGroupString = matchGroupString?.trim() ?? '';
|
||||||
|
if (matchGroupString.isEmpty) {
|
||||||
|
matchGroupString = "0";
|
||||||
|
}
|
||||||
|
version = replaceMatchGroupsInString(match.last, matchGroupString);
|
||||||
|
if (version?.isNotEmpty != true) {
|
||||||
|
throw NoVersionError();
|
||||||
|
}
|
||||||
|
return version!;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MapEntry<String, String>> filterApks(
|
||||||
|
List<MapEntry<String, String>> apkUrls,
|
||||||
|
String? apkFilterRegEx,
|
||||||
|
bool? invert) {
|
||||||
|
if (apkFilterRegEx?.isNotEmpty == true) {
|
||||||
|
var reg = RegExp(apkFilterRegEx!);
|
||||||
|
apkUrls = apkUrls.where((element) {
|
||||||
|
var hasMatch = reg.hasMatch(element.key);
|
||||||
|
return invert == true ? !hasMatch : hasMatch;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
return apkUrls;
|
||||||
|
}
|
||||||
|
|
||||||
class SourceProvider {
|
class SourceProvider {
|
||||||
// Add more source classes here so they are available via the service
|
// Add more source classes here so they are available via the service
|
||||||
List<AppSource> get sources => [
|
List<AppSource> get sources => [
|
||||||
@@ -552,14 +744,12 @@ class SourceProvider {
|
|||||||
APKMirror(),
|
APKMirror(),
|
||||||
HuaweiAppGallery(),
|
HuaweiAppGallery(),
|
||||||
Jenkins(),
|
Jenkins(),
|
||||||
// APKCombo(), // Can't get past their scraping blocking yet (get 403 Forbidden)
|
|
||||||
Mullvad(),
|
|
||||||
Signal(),
|
Signal(),
|
||||||
VLC(),
|
VLC(),
|
||||||
// WhatsApp(), // As of 2023-03-20 this is unusable as the version on the webpage is months out of date
|
WhatsApp(),
|
||||||
TelegramApp(),
|
TelegramApp(),
|
||||||
SteamMobile(),
|
|
||||||
NeutronCode(),
|
NeutronCode(),
|
||||||
|
DirectAPKLink(),
|
||||||
HTML() // This should ALWAYS be the last option as they are tried in order
|
HTML() // This should ALWAYS be the last option as they are tried in order
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -575,21 +765,26 @@ class SourceProvider {
|
|||||||
throw UnsupportedURLError();
|
throw UnsupportedURLError();
|
||||||
}
|
}
|
||||||
var res = srcs.first;
|
var res = srcs.first;
|
||||||
res.host = Uri.parse(url).host;
|
res.hosts = [Uri.parse(url).host];
|
||||||
res.hostChanged = true;
|
res.hostChanged = true;
|
||||||
return srcs.first;
|
return srcs.first;
|
||||||
}
|
}
|
||||||
AppSource? source;
|
AppSource? source;
|
||||||
for (var s in sources.where((element) => element.host != null)) {
|
for (var s in sources.where((element) => element.hosts.isNotEmpty)) {
|
||||||
if (RegExp(
|
try {
|
||||||
'://${s.allowSubDomains ? '([^\\.]+\\.)*' : ''}${s.host}(/|\\z)?')
|
if (RegExp(
|
||||||
.hasMatch(url)) {
|
'^${s.allowSubDomains ? '([^\\.]+\\.)*' : '(www\\.)?'}(${getSourceRegex(s.hosts)})\$')
|
||||||
source = s;
|
.hasMatch(Uri.parse(url).host)) {
|
||||||
break;
|
source = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
for (var s in sources.where((element) => element.host == null)) {
|
for (var s in sources.where(
|
||||||
|
(element) => element.hosts.isEmpty && !element.neverAutoSelect)) {
|
||||||
try {
|
try {
|
||||||
s.sourceSpecificStandardizeURL(url);
|
s.sourceSpecificStandardizeURL(url);
|
||||||
source = s;
|
source = s;
|
||||||
@@ -620,11 +815,6 @@ class SourceProvider {
|
|||||||
String standardUrl, Map<String, dynamic> additionalSettings) =>
|
String standardUrl, Map<String, dynamic> additionalSettings) =>
|
||||||
(standardUrl + additionalSettings.toString()).hashCode.toString();
|
(standardUrl + additionalSettings.toString()).hashCode.toString();
|
||||||
|
|
||||||
bool isTempId(App app) {
|
|
||||||
// return app.id == generateTempID(app.url, app.additionalSettings);
|
|
||||||
return RegExp('^[0-9]+\$').hasMatch(app.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<App> getApp(
|
Future<App> getApp(
|
||||||
AppSource source, String url, Map<String, dynamic> additionalSettings,
|
AppSource source, String url, Map<String, dynamic> additionalSettings,
|
||||||
{App? currentApp,
|
{App? currentApp,
|
||||||
@@ -638,15 +828,24 @@ class SourceProvider {
|
|||||||
String standardUrl = source.standardizeUrl(url);
|
String standardUrl = source.standardizeUrl(url);
|
||||||
APKDetails apk =
|
APKDetails apk =
|
||||||
await source.getLatestAPKDetails(standardUrl, additionalSettings);
|
await source.getLatestAPKDetails(standardUrl, additionalSettings);
|
||||||
if (additionalSettings['versionDetection'] == 'releaseDateAsVersion' &&
|
|
||||||
|
if (source.runtimeType != HTML().runtimeType) {
|
||||||
|
// HTML does it separately
|
||||||
|
String? extractedVersion = extractVersion(
|
||||||
|
additionalSettings['versionExtractionRegEx'] as String?,
|
||||||
|
additionalSettings['matchGroupToUse'] as String?,
|
||||||
|
apk.version);
|
||||||
|
if (extractedVersion != null) {
|
||||||
|
apk.version = extractedVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (additionalSettings['releaseDateAsVersion'] == true &&
|
||||||
apk.releaseDate != null) {
|
apk.releaseDate != null) {
|
||||||
apk.version = apk.releaseDate!.microsecondsSinceEpoch.toString();
|
apk.version = apk.releaseDate!.microsecondsSinceEpoch.toString();
|
||||||
}
|
}
|
||||||
if (additionalSettings['apkFilterRegEx'] != null) {
|
apk.apkUrls = filterApks(apk.apkUrls, additionalSettings['apkFilterRegEx'],
|
||||||
var reg = RegExp(additionalSettings['apkFilterRegEx']);
|
additionalSettings['invertAPKFilter']);
|
||||||
apk.apkUrls =
|
|
||||||
apk.apkUrls.where((element) => reg.hasMatch(element.key)).toList();
|
|
||||||
}
|
|
||||||
if (apk.apkUrls.isEmpty && !trackOnly) {
|
if (apk.apkUrls.isEmpty && !trackOnly) {
|
||||||
throw NoAPKError();
|
throw NoAPKError();
|
||||||
}
|
}
|
||||||
@@ -663,13 +862,13 @@ class SourceProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String apkVersion = apk.version.replaceAll('/', '-');
|
|
||||||
var name = currentApp != null ? currentApp.name.trim() : '';
|
var name = currentApp != null ? currentApp.name.trim() : '';
|
||||||
name = name.isNotEmpty ? name : apk.names.name;
|
name = name.isNotEmpty ? name : apk.names.name;
|
||||||
return App(
|
App finalApp = App(
|
||||||
currentApp?.id ??
|
currentApp?.id ??
|
||||||
((!source.appIdInferIsOptional ||
|
(!trackOnly &&
|
||||||
(source.appIdInferIsOptional && inferAppIdIfOptional))
|
(!source.appIdInferIsOptional ||
|
||||||
|
(source.appIdInferIsOptional && inferAppIdIfOptional))
|
||||||
? await source.tryInferringAppId(standardUrl,
|
? await source.tryInferringAppId(standardUrl,
|
||||||
additionalSettings: additionalSettings)
|
additionalSettings: additionalSettings)
|
||||||
: null) ??
|
: null) ??
|
||||||
@@ -678,7 +877,7 @@ class SourceProvider {
|
|||||||
apk.names.author,
|
apk.names.author,
|
||||||
name,
|
name,
|
||||||
currentApp?.installedVersion,
|
currentApp?.installedVersion,
|
||||||
apkVersion,
|
apk.version,
|
||||||
apk.apkUrls,
|
apk.apkUrls,
|
||||||
apk.apkUrls.length - 1 >= 0 ? apk.apkUrls.length - 1 : 0,
|
apk.apkUrls.length - 1 >= 0 ? apk.apkUrls.length - 1 : 0,
|
||||||
additionalSettings,
|
additionalSettings,
|
||||||
@@ -689,14 +888,17 @@ class SourceProvider {
|
|||||||
changeLog: apk.changeLog,
|
changeLog: apk.changeLog,
|
||||||
overrideSource: overrideSource ?? currentApp?.overrideSource,
|
overrideSource: overrideSource ?? currentApp?.overrideSource,
|
||||||
allowIdChange: currentApp?.allowIdChange ??
|
allowIdChange: currentApp?.allowIdChange ??
|
||||||
source.appIdInferIsOptional &&
|
trackOnly ||
|
||||||
inferAppIdIfOptional // Optional ID inferring may be incorrect - allow correction on first install
|
(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
|
// Returns errors in [results, errors] instead of throwing them
|
||||||
Future<List<dynamic>> getAppsByURLNaive(List<String> urls,
|
Future<List<dynamic>> getAppsByURLNaive(List<String> urls,
|
||||||
{List<String> alreadyAddedUrls = const []}) async {
|
{List<String> alreadyAddedUrls = const [],
|
||||||
|
AppSource? sourceOverride}) async {
|
||||||
List<App> apps = [];
|
List<App> apps = [];
|
||||||
Map<String, dynamic> errors = {};
|
Map<String, dynamic> errors = {};
|
||||||
for (var url in urls) {
|
for (var url in urls) {
|
||||||
@@ -704,7 +906,7 @@ class SourceProvider {
|
|||||||
if (alreadyAddedUrls.contains(url)) {
|
if (alreadyAddedUrls.contains(url)) {
|
||||||
throw ObtainiumError(tr('appAlreadyAdded'));
|
throw ObtainiumError(tr('appAlreadyAdded'));
|
||||||
}
|
}
|
||||||
var source = getSource(url);
|
var source = sourceOverride ?? getSource(url);
|
||||||
apps.add(await getApp(
|
apps.add(await getApp(
|
||||||
source,
|
source,
|
||||||
url,
|
url,
|
||||||
|
|||||||
342
pubspec.lock
@@ -1,22 +1,14 @@
|
|||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
android_alarm_manager_plus:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: android_alarm_manager_plus
|
|
||||||
sha256: c20d91a9096596f66274bf8172321c278f9cba8091638f80205fe66d31587fa5
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.2"
|
|
||||||
android_intent_plus:
|
android_intent_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: android_intent_plus
|
name: android_intent_plus
|
||||||
sha256: f72ae20bb37108694f442e7ae6acbd28b453ca62ce86842f6787b784355abfe6
|
sha256: e1c62bb41c90e15083b7fb84dc327fe90396cc9c1445b55ff1082144fabfb4d9
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.3"
|
||||||
android_package_installer:
|
android_package_installer:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -30,26 +22,34 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: android_package_manager
|
name: android_package_manager
|
||||||
sha256: b873fe5856f7c442aca9751dac05d117285be9e4de08eb15d1ffb811fd1b688d
|
sha256: e52ca607b9f19f95d5dae4211ed8fa93e67093f22ac570db47489c5bca512940
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.7.0"
|
||||||
animations:
|
animations:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: animations
|
name: animations
|
||||||
sha256: ef57563eed3620bd5d75ad96189846aca1e033c0c45fc9a7d26e80ab02b88a70
|
sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.8"
|
version: "2.0.11"
|
||||||
|
app_links:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: app_links
|
||||||
|
sha256: "4e392b5eba997df356ca6021f28431ce1cfeb16758699553a94b13add874a3bb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.5.0"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "20071638cbe4e5964a427cfa0e86dce55d060bc7d82d56f3554095d7239a8765"
|
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.2"
|
version: "3.4.10"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -66,6 +66,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.0"
|
version: "2.11.0"
|
||||||
|
background_fetch:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: background_fetch
|
||||||
|
sha256: "34550cf9b383e5a1844e7d22119aa500508c7df9421fa967c9fb4430d6cb2878"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.2"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -94,10 +102,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cli_util
|
name: cli_util
|
||||||
sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7
|
sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.0"
|
version: "0.4.1"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -110,18 +118,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.17.2"
|
version: "1.18.0"
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: connectivity_plus
|
name: connectivity_plus
|
||||||
sha256: "77a180d6938f78ca7d2382d2240eb626c0f6a735d0bfdce227d8ffb80f95c48b"
|
sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "5.0.2"
|
||||||
connectivity_plus_platform_interface:
|
connectivity_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -142,12 +150,12 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cross_file
|
name: cross_file
|
||||||
sha256: fd832b5384d0d6da4f6df60b854d33accaaeb63aa9e10e736a87381f08dee2cb
|
sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3+5"
|
version: "0.3.3+8"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||||
@@ -174,18 +182,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dbus
|
name: dbus
|
||||||
sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263"
|
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.8"
|
version: "0.7.10"
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: device_info_plus
|
name: device_info_plus
|
||||||
sha256: "86add5ef97215562d2e090535b0a16f197902b10c369c558a100e74ea06e8659"
|
sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.0.3"
|
version: "9.1.1"
|
||||||
device_info_plus_platform_interface:
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -198,10 +206,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dynamic_color
|
name: dynamic_color
|
||||||
sha256: "96bff3df72e3d428bda2b874c7a521e8c86f592cae626ea594922fcc8d166e0c"
|
sha256: a866f1f8947bfdaf674d7928e769eac7230388a2e7a2542824fad4bb5b87be3b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.7"
|
version: "1.6.9"
|
||||||
easy_localization:
|
easy_localization:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -238,18 +246,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file
|
name: file
|
||||||
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.4"
|
version: "7.0.0"
|
||||||
file_picker:
|
file_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: file_picker
|
name: file_picker
|
||||||
sha256: be325344c1f3070354a1d84a231a1ba75ea85d413774ec4bdf444c023342e030
|
sha256: "4e42aacde3b993c5947467ab640882c56947d9d27342a5b6f2895b23956954a6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.5.0"
|
version: "6.1.1"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -259,10 +275,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_archive
|
name: flutter_archive
|
||||||
sha256: aec85d1da65e5b33a529db00a86df0b8e92bda78088a7cfaeeba5187701d0d85
|
sha256: "004132780d382df5171589ab793e2efc9c3eef570fe72d78b4ccfbfbe52762ae"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "6.0.0"
|
||||||
flutter_fgbg:
|
flutter_fgbg:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -283,18 +299,18 @@ packages:
|
|||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: flutter_lints
|
name: flutter_lints
|
||||||
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
|
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "3.0.1"
|
||||||
flutter_local_notifications:
|
flutter_local_notifications:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_local_notifications
|
name: flutter_local_notifications
|
||||||
sha256: "3002092e5b8ce2f86c3361422e52e6db6776c23ee21e0b2f71b892bf4259ef04"
|
sha256: c18f1de98fe0bb9dd5ba91e1330d4febc8b6a7de6aae3ffe475ef423723e72f3
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "15.1.1"
|
version: "16.3.2"
|
||||||
flutter_local_notifications_linux:
|
flutter_local_notifications_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -320,18 +336,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_markdown
|
name: flutter_markdown
|
||||||
sha256: a10979814c5f4ddbe2b6143fba25d927599e21e3ba65b3862995960606fae78f
|
sha256: "30088ce826b5b9cfbf9e8bece34c716c8a59fa54461dcae1e4ac01a94639e762"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.17+3"
|
version: "0.6.18+3"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_plugin_android_lifecycle
|
name: flutter_plugin_android_lifecycle
|
||||||
sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c
|
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.16"
|
version: "2.0.17"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -346,10 +362,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: fluttertoast
|
name: fluttertoast
|
||||||
sha256: "474f7d506230897a3cd28c965ec21c5328ae5605fc9c400cd330e9e9d6ac175c"
|
sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.2.2"
|
version: "8.2.4"
|
||||||
|
gtk:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: gtk
|
||||||
|
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
hsluv:
|
hsluv:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -370,10 +394,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.2.0"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -386,10 +410,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: "6e703d5e2f8c63fb31a77753915c1ec8baebde8088844e0d29f71b8f0b108888"
|
sha256: "004a2e90ce080f8627b5a04aecb4cdfac87d2c3f3b520aa291260be5a32c033d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.0"
|
version: "4.1.4"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -418,18 +442,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: lints
|
name: lints
|
||||||
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
|
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "3.0.0"
|
||||||
markdown:
|
markdown:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: markdown
|
name: markdown
|
||||||
sha256: acf35edccc0463a9d7384e437c015a3535772e09714cf60e07eeef3a15870dcd
|
sha256: "1b134d9f8ff2da15cb298efe6cd8b7d2a78958c1b00384ebcbdf13fe340a6c90"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.1.1"
|
version: "7.2.1"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -450,18 +474,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.10.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: mime
|
name: mime
|
||||||
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.5"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -490,26 +514,26 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
|
sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1"
|
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.2"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_foundation
|
name: path_provider_foundation
|
||||||
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
|
sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.2"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -522,10 +546,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_platform_interface
|
name: path_provider_platform_interface
|
||||||
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
|
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
path_provider_windows:
|
path_provider_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -538,106 +562,114 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: permission_handler
|
name: permission_handler
|
||||||
sha256: ad65ba9af42a3d067203641de3fd9f547ded1410bad3b84400c2b4899faede70
|
sha256: "45ff3fbcb99040fde55c528d5e3e6ca29171298a85436274d49c6201002087d6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.0.0"
|
version: "11.2.0"
|
||||||
permission_handler_android:
|
permission_handler_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_android
|
name: permission_handler_android
|
||||||
sha256: f2543a236584a5e8be79076f858022f100ce690e31530e6fa4c32ac94f276d3a
|
sha256: "758284a0976772f9c744d6384fc5dc4834aa61e3f7aa40492927f244767374eb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.0.3"
|
version: "12.0.3"
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_apple
|
name: permission_handler_apple
|
||||||
sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
|
sha256: c6bf440f80acd2a873d3d91a699e4cc770f86e7e6b576dda98759e8b92b39830
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.1.4"
|
version: "9.3.0"
|
||||||
|
permission_handler_html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_html
|
||||||
|
sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.1"
|
||||||
permission_handler_platform_interface:
|
permission_handler_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_platform_interface
|
name: permission_handler_platform_interface
|
||||||
sha256: f2343e9fa9c22ae4fd92d4732755bfe452214e7189afcc097380950cf567b4b2
|
sha256: "5c43148f2bfb6d14c5a8162c0a712afe891f2d847f35fcff29c406b37da43c3c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.11.5"
|
version: "4.1.0"
|
||||||
permission_handler_windows:
|
permission_handler_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_windows
|
name: permission_handler_windows
|
||||||
sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
|
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.3"
|
version: "0.2.1"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: petitparser
|
name: petitparser
|
||||||
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
|
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.0"
|
version: "6.0.2"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
|
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.4"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.6"
|
version: "2.1.8"
|
||||||
pointycastle:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pointycastle
|
name: pointycastle
|
||||||
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
|
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.7.3"
|
version: "3.7.4"
|
||||||
provider:
|
provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: provider
|
name: provider
|
||||||
sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f
|
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.5"
|
version: "6.1.1"
|
||||||
share_plus:
|
share_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: share_plus
|
name: share_plus
|
||||||
sha256: "6cec740fa0943a826951223e76218df002804adb588235a8910dc3d6b0654e11"
|
sha256: f74fc3f1cbd99f39760182e176802f693fa0ec9625c045561cfad54681ea93dd
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.1.0"
|
version: "7.2.1"
|
||||||
share_plus_platform_interface:
|
share_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: share_plus_platform_interface
|
name: share_plus_platform_interface
|
||||||
sha256: "357412af4178d8e11d14f41723f80f12caea54cf0d5cd29af9dcdab85d58aea7"
|
sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.0"
|
version: "3.3.1"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
sha256: b7f41bad7e521d205998772545de63ff4e6c97714775902c199353f8bf1511ac
|
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.2"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -650,42 +682,42 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_foundation
|
name: shared_preferences_foundation
|
||||||
sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
|
sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.4"
|
version: "2.3.5"
|
||||||
shared_preferences_linux:
|
shared_preferences_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_linux
|
name: shared_preferences_linux
|
||||||
sha256: c2eb5bf57a2fe9ad6988121609e47d3e07bb3bdca5b6f8444e4cf302428a128a
|
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.2"
|
||||||
shared_preferences_platform_interface:
|
shared_preferences_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_platform_interface
|
name: shared_preferences_platform_interface
|
||||||
sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
|
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.2"
|
||||||
shared_preferences_web:
|
shared_preferences_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_web
|
name: shared_preferences_web
|
||||||
sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf
|
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.2"
|
||||||
shared_preferences_windows:
|
shared_preferences_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_windows
|
name: shared_preferences_windows
|
||||||
sha256: f763a101313bd3be87edffe0560037500967de9c394a714cd598d945517f694f
|
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.2"
|
||||||
shared_storage:
|
shared_storage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -707,38 +739,46 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.10.0"
|
||||||
|
sprintf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sprintf
|
||||||
|
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
sqflite:
|
sqflite:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: sqflite
|
name: sqflite
|
||||||
sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a"
|
sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.2"
|
||||||
sqflite_common:
|
sqflite_common:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sqflite_common
|
name: sqflite_common
|
||||||
sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a"
|
sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.0"
|
version: "2.5.3"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.0"
|
version: "1.11.1"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -751,10 +791,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: synchronized
|
name: synchronized
|
||||||
sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
|
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0+1"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -767,10 +807,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.6.1"
|
||||||
timezone:
|
timezone:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -791,74 +831,74 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27"
|
sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.14"
|
version: "6.2.4"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330
|
sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.0"
|
version: "6.2.2"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_ios
|
name: url_launcher_ios
|
||||||
sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f"
|
sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.5"
|
version: "6.2.4"
|
||||||
url_launcher_linux:
|
url_launcher_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_linux
|
name: url_launcher_linux
|
||||||
sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e
|
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.6"
|
version: "3.1.1"
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_macos
|
name: url_launcher_macos
|
||||||
sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88
|
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.7"
|
version: "3.1.0"
|
||||||
url_launcher_platform_interface:
|
url_launcher_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_platform_interface
|
name: url_launcher_platform_interface
|
||||||
sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618"
|
sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.5"
|
version: "2.3.1"
|
||||||
url_launcher_web:
|
url_launcher_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5"
|
sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.20"
|
version: "2.2.3"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_windows
|
name: url_launcher_windows
|
||||||
sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069"
|
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.8"
|
version: "3.1.1"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
|
sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.7"
|
version: "4.3.3"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -871,50 +911,50 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.4-beta"
|
version: "0.3.0"
|
||||||
webview_flutter:
|
webview_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: webview_flutter
|
name: webview_flutter
|
||||||
sha256: "82f6787d5df55907aa01e49bd9644f4ed1cc82af7a8257dd9947815959d2e755"
|
sha256: "71e1bfaef41016c8d5954291df5e9f8c6172f1f6ff3af01b5656456ddb11f94c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.4"
|
version: "4.4.4"
|
||||||
webview_flutter_android:
|
webview_flutter_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_android
|
name: webview_flutter_android
|
||||||
sha256: ddc167c6676f57c8b367d19fcbee267d6dc6adf81bd6c3cb87981d30746e0a6d
|
sha256: "4ea3c4e1b8ed590162b15b8a61b41b1ef3ff179a314627c16ce40c086d94b8af"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.10.1"
|
version: "3.14.0"
|
||||||
webview_flutter_platform_interface:
|
webview_flutter_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_platform_interface
|
name: webview_flutter_platform_interface
|
||||||
sha256: "6d9213c65f1060116757a7c473247c60f3f7f332cac33dc417c9e362a9a13e4f"
|
sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.0"
|
version: "2.10.0"
|
||||||
webview_flutter_wkwebview:
|
webview_flutter_wkwebview:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_wkwebview
|
name: webview_flutter_wkwebview
|
||||||
sha256: "485af05f2c5f83c7f78c20e236b170ad02df7153b299ae9917345be43871d29f"
|
sha256: b99ca8d8bae9c6b43d568218691aa537fb0aeae1d7d34eadf112a6aa36d26506
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.8.0"
|
version: "3.11.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: c97defd418eef4ec88c0d1652cdce84b9f7b63dd7198e266d06ac1710d527067
|
sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.8"
|
version: "5.2.0"
|
||||||
win32_registry:
|
win32_registry:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -927,18 +967,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
|
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.0.4"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xml
|
name: xml
|
||||||
sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
|
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.0"
|
version: "6.5.0"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -948,5 +988,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.1.0 <4.0.0"
|
dart: ">=3.2.3 <4.0.0"
|
||||||
flutter: ">=3.13.0"
|
flutter: ">=3.16.6"
|
||||||
|
|||||||
18
pubspec.yaml
@@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 0.14.19+211 # When changing this, update the tag in main() accordingly
|
version: 1.0.1+251 # When changing this, update the tag in main() accordingly
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.0.0 <4.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
@@ -38,7 +38,7 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.5
|
cupertino_icons: ^1.0.5
|
||||||
path_provider: ^2.0.11
|
path_provider: ^2.0.11
|
||||||
flutter_fgbg: ^0.3.0 # Try removing reliance on this
|
flutter_fgbg: ^0.3.0 # Try removing reliance on this
|
||||||
flutter_local_notifications: ^15.1.0+1
|
flutter_local_notifications: ^16.1.0
|
||||||
provider: ^6.0.3
|
provider: ^6.0.3
|
||||||
http: ^1.0.0
|
http: ^1.0.0
|
||||||
webview_flutter: ^4.0.0
|
webview_flutter: ^4.0.0
|
||||||
@@ -49,23 +49,25 @@ dependencies:
|
|||||||
permission_handler: ^11.0.0
|
permission_handler: ^11.0.0
|
||||||
fluttertoast: ^8.0.9
|
fluttertoast: ^8.0.9
|
||||||
device_info_plus: ^9.0.0
|
device_info_plus: ^9.0.0
|
||||||
file_picker: ^5.2.10
|
file_picker: ^6.0.0
|
||||||
animations: ^2.0.4
|
animations: ^2.0.4
|
||||||
android_package_installer:
|
android_package_installer:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/ImranR98/android_package_installer
|
url: https://github.com/ImranR98/android_package_installer
|
||||||
ref: main
|
ref: main
|
||||||
android_package_manager: ^0.6.0
|
android_package_manager: ^0.7.0
|
||||||
share_plus: ^7.0.0
|
share_plus: ^7.0.0
|
||||||
android_alarm_manager_plus: ^3.0.0
|
|
||||||
sqflite: ^2.2.0+3
|
sqflite: ^2.2.0+3
|
||||||
easy_localization: ^3.0.1
|
easy_localization: ^3.0.1
|
||||||
android_intent_plus: ^4.0.0
|
android_intent_plus: ^4.0.0
|
||||||
flutter_markdown: ^0.6.14
|
flutter_markdown: ^0.6.14
|
||||||
flutter_archive: ^5.0.0
|
flutter_archive: ^6.0.0
|
||||||
hsluv: ^1.1.3
|
hsluv: ^1.1.3
|
||||||
connectivity_plus: ^4.0.2
|
connectivity_plus: ^5.0.0
|
||||||
shared_storage: ^0.8.0
|
shared_storage: ^0.8.0
|
||||||
|
crypto: ^3.0.3
|
||||||
|
app_links: ^3.5.0
|
||||||
|
background_fetch: ^1.2.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -77,7 +79,7 @@ dev_dependencies:
|
|||||||
# activated in the `analysis_options.yaml` file located at the root of your
|
# activated in the `analysis_options.yaml` file located at the root of your
|
||||||
# package. See that file for information about deactivating specific lint
|
# package. See that file for information about deactivating specific lint
|
||||||
# rules and activating additional ones.
|
# rules and activating additional ones.
|
||||||
flutter_lints: ^2.0.1
|
flutter_lints: ^3.0.0
|
||||||
|
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
android: "ic_launcher"
|
android: "ic_launcher"
|
||||||
|
|||||||