mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
Compare commits
637
Commits
v1.3.12
...
v4.0.2-rc0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83b24cced2 | ||
|
|
d913e72c5c | ||
|
|
5c16c08dee | ||
|
|
20e22c0910 | ||
|
|
fa3e56b0f3 | ||
|
|
f25718ab2c | ||
|
|
6e9b275936 | ||
|
|
c6c7cd7858 | ||
|
|
4d7027a9b8 | ||
|
|
992cb5e695 | ||
|
|
039d20d7aa | ||
|
|
52433460be | ||
|
|
95bb225e64 | ||
|
|
ac477113cf | ||
|
|
5e21941765 | ||
|
|
e5032ee35a | ||
|
|
1ceac349ab | ||
|
|
2db01b410b | ||
|
|
193a1030fb | ||
|
|
12e35631d6 | ||
|
|
67474a30da | ||
|
|
44cd0eec2a | ||
|
|
a79f6a0535 | ||
|
|
8675d73a98 | ||
|
|
3423786f06 | ||
|
|
00083707c6 | ||
|
|
0cdd2a4f7a | ||
|
|
8e3b235c81 | ||
|
|
879ec1b69c | ||
|
|
c833fd2c92 | ||
|
|
d7d9d53ff3 | ||
|
|
cba239f411 | ||
|
|
853e877a59 | ||
|
|
de89c51457 | ||
|
|
7f8daddfe0 | ||
|
|
a76e43ac04 | ||
|
|
fd0dc49396 | ||
|
|
a5f5feacae | ||
|
|
70a3114876 | ||
|
|
557ee9b3ed | ||
|
|
58bd0f91f5 | ||
|
|
b2f8586dc9 | ||
|
|
62797e8ea2 | ||
|
|
c614e7bd4f | ||
|
|
62e39ce3d2 | ||
|
|
7df74e3b44 | ||
|
|
06e5954ef5 | ||
|
|
450a187b65 | ||
|
|
da6a6f6ff2 | ||
|
|
d37c8aa3a5 | ||
|
|
5cd97805b4 | ||
|
|
e35bce4d19 | ||
|
|
1a196689db | ||
|
|
8ada7b071d | ||
|
|
a92e364ca6 | ||
|
|
6326f4a5b9 | ||
|
|
abb189f43c | ||
|
|
e80bf1db6c | ||
|
|
cd6488a713 | ||
|
|
e13331dfcf | ||
|
|
dcf2ade9ec | ||
|
|
40f0aba462 | ||
|
|
c760d5758d | ||
|
|
b609c1aced | ||
|
|
b3b55f1ca3 | ||
|
|
073de3cf35 | ||
|
|
8c278f2a3c | ||
|
|
c103c6adc9 | ||
|
|
4db2edd29a | ||
|
|
eefe740b6f | ||
|
|
a47addc8bc | ||
|
|
9cc5a82adf | ||
|
|
bec24bfd6f | ||
|
|
e6eb0d818f | ||
|
|
25e3e7d792 | ||
|
|
ebcb997bdd | ||
|
|
76c55bf3be | ||
|
|
fc9bcafae9 | ||
|
|
c4e58917da | ||
|
|
3730a78e7a | ||
|
|
222a6c43c9 | ||
|
|
b1fe8490d3 | ||
|
|
a0fe20e296 | ||
|
|
e2d127dae9 | ||
|
|
b5c48be08e | ||
|
|
90f0c89b79 | ||
|
|
8dd165e1c8 | ||
|
|
119139cbc5 | ||
|
|
5159e4c21b | ||
|
|
1c94354831 | ||
|
|
75d2bf0169 | ||
|
|
7e73777469 | ||
|
|
3d07c284b9 | ||
|
|
f6b1c69edc | ||
|
|
f421deca49 | ||
|
|
e19608f567 | ||
|
|
06f4115808 | ||
|
|
2ca8f686c8 | ||
|
|
679250f7da | ||
|
|
8df2d78070 | ||
|
|
63692595a8 | ||
|
|
576c6db8d0 | ||
|
|
524cac8d30 | ||
|
|
8939f788ad | ||
|
|
9ad3f3c180 | ||
|
|
5b59ddab0f | ||
|
|
ab24c48024 | ||
|
|
a6482c7d76 | ||
|
|
99aa44b448 | ||
|
|
a801f2d701 | ||
|
|
8405406d22 | ||
|
|
9785b5ea7f | ||
|
|
fd7f9f8ab3 | ||
|
|
46bb8c98de | ||
|
|
b3ead17b12 | ||
|
|
a794d59276 | ||
|
|
584c5c70d0 | ||
|
|
5a55796518 | ||
|
|
bdfdd93735 | ||
|
|
e9a937e4f6 | ||
|
|
0a8bae46a9 | ||
|
|
7efcafbc71 | ||
|
|
6b3f431e9e | ||
|
|
6014004c0f | ||
|
|
ebbc5394a7 | ||
|
|
e36868a2fc | ||
|
|
1c6fb8ed31 | ||
|
|
e780fd17d2 | ||
|
|
86d3f3bc61 | ||
|
|
ba554c8787 | ||
|
|
968829072a | ||
|
|
0d1f331551 | ||
|
|
a8bb42d40b | ||
|
|
9a3fb11ef7 | ||
|
|
12d2c4dd06 | ||
|
|
cfed733fa7 | ||
|
|
1af8743533 | ||
|
|
2f3f1f3f70 | ||
|
|
6223524a1e | ||
|
|
ebfffb1536 | ||
|
|
5aa36b148b | ||
|
|
29391feaf8 | ||
|
|
4e279ca44d | ||
|
|
9ff8d9e04e | ||
|
|
10d5a823fc | ||
|
|
54afadb136 | ||
|
|
e4db9387ca | ||
|
|
4834eb276a | ||
|
|
5860bbffb6 | ||
|
|
da53d68190 | ||
|
|
b397bf190c | ||
|
|
15d664c361 | ||
|
|
d2f24d8b95 | ||
|
|
b5436659b6 | ||
|
|
1cfc4611cb | ||
|
|
bda0a20743 | ||
|
|
09bad90c0c | ||
|
|
34856607f8 | ||
|
|
5c2b66fe27 | ||
|
|
c9fe9a43e2 | ||
|
|
395dbbb624 | ||
|
|
fdc6486102 | ||
|
|
6c379451c0 | ||
|
|
221f3ccbed | ||
|
|
12f0662146 | ||
|
|
7cc81bd554 | ||
|
|
618fddb698 | ||
|
|
0f74cb9b39 | ||
|
|
743f5aeb89 | ||
|
|
9260a24e0d | ||
|
|
49fc0f018a | ||
|
|
ad7089ea7c | ||
|
|
e74b83581a | ||
|
|
a3fb26f967 | ||
|
|
2b4161b9e7 | ||
|
|
d38f2ea3e9 | ||
|
|
ba4445f19d | ||
|
|
33cab939f0 | ||
|
|
69ce7e18f5 | ||
|
|
e2014a26fc | ||
|
|
1e806ea416 | ||
|
|
6cf85935f9 | ||
|
|
25b838da5c | ||
|
|
03676346e9 | ||
|
|
f2e6d8978c | ||
|
|
fbbbf751c5 | ||
|
|
132b3996fc | ||
|
|
7b41f83a53 | ||
|
|
b6027186c0 | ||
|
|
d2d126e9d9 | ||
|
|
89632d864a | ||
|
|
2cd209ee03 | ||
|
|
b96d6ef4db | ||
|
|
d6af8e21cc | ||
|
|
ae2d1c6868 | ||
|
|
98cf4ff3e7 | ||
|
|
679c9cd070 | ||
|
|
5b281303d9 | ||
|
|
cb28bbfaf3 | ||
|
|
cd190389cd | ||
|
|
0af469af07 | ||
|
|
1689153070 | ||
|
|
520458b107 | ||
|
|
be8f4919c8 | ||
|
|
deb012600d | ||
|
|
15371e01ef | ||
|
|
0e3ee655ef | ||
|
|
da5af8e4b1 | ||
|
|
6c848eaa55 | ||
|
|
15054d836d | ||
|
|
0ac9bba482 | ||
|
|
247bc13e0a | ||
|
|
33126be817 | ||
|
|
b02b31043b | ||
|
|
6786b5aa8a | ||
|
|
8577e7bae8 | ||
|
|
df88399e7a | ||
|
|
16392d5721 | ||
|
|
2edf5411ce | ||
|
|
89e46d3858 | ||
|
|
da45eb5dda | ||
|
|
a1868f495d | ||
|
|
ceb5034c3b | ||
|
|
02ff7089c0 | ||
|
|
2fa33b13a3 | ||
|
|
96a654638a | ||
|
|
b4aa7b0815 | ||
|
|
df5389c27e | ||
|
|
0611ec0e08 | ||
|
|
c325112b44 | ||
|
|
65f9e1d9db | ||
|
|
c97c8f4852 | ||
|
|
6b2073eff2 | ||
|
|
2b08d9a4d0 | ||
|
|
977f135c50 | ||
|
|
3c777d377e | ||
|
|
b98994f2f1 | ||
|
|
85860a53bf | ||
|
|
7a967ce648 | ||
|
|
4c7eac870d | ||
|
|
ff58e01551 | ||
|
|
004ea6cbcb | ||
|
|
a42b88aa78 | ||
|
|
43088cf8b8 | ||
|
|
9460584891 | ||
|
|
570118ca94 | ||
|
|
d9f0de2e05 | ||
|
|
3d5dcfb21d | ||
|
|
e0dc9ea737 | ||
|
|
b4d0904f55 | ||
|
|
2caee59713 | ||
|
|
6712ba78ba | ||
|
|
cba30aee47 | ||
|
|
c6cd8d68f7 | ||
|
|
d2fbf9d79f | ||
|
|
738a35dbe9 | ||
|
|
1278ec55c2 | ||
|
|
95ff34b373 | ||
|
|
1329429130 | ||
|
|
8c3c09dc03 | ||
|
|
10bf65bd09 | ||
|
|
b57b3458ab | ||
|
|
6aeee4816c | ||
|
|
3c8f24fa50 | ||
|
|
5e60629b4c | ||
|
|
21d740ae51 | ||
|
|
f4e8f08f4f | ||
|
|
7bf472023f | ||
|
|
01fc9654f8 | ||
|
|
c1b247c219 | ||
|
|
101fb68205 | ||
|
|
cda16ca06a | ||
|
|
e9b29a2a43 | ||
|
|
5df809a492 | ||
|
|
ffe8a58746 | ||
|
|
eab0296125 | ||
|
|
2ff143b858 | ||
|
|
3b2dcc1a5c | ||
|
|
d59dc8c225 | ||
|
|
877c5d9013 | ||
|
|
11a19da0b8 | ||
|
|
eda81822aa | ||
|
|
3b00613dde | ||
|
|
a434a0bee0 | ||
|
|
33ca43eaf1 | ||
|
|
df4ec3501b | ||
|
|
fc54e540e8 | ||
|
|
e8481cc8cd | ||
|
|
eb8a9b3b46 | ||
|
|
79ec3ba932 | ||
|
|
8f910b6dcc | ||
|
|
92fa181925 | ||
|
|
ca82f96b30 | ||
|
|
2ea2bb1765 | ||
|
|
fb22798469 | ||
|
|
d48f475a34 | ||
|
|
c88c57e856 | ||
|
|
17892fbd24 | ||
|
|
2352974d4d | ||
|
|
e216fbf66e | ||
|
|
f3a947a8a3 | ||
|
|
00264c1d2b | ||
|
|
54a02837fd | ||
|
|
7a5af76ebb | ||
|
|
92bd754623 | ||
|
|
85b9978e05 | ||
|
|
1ebb2319d4 | ||
|
|
f956625d2e | ||
|
|
9c2ebd9d04 | ||
|
|
fb0527b0bf | ||
|
|
6b922e8845 | ||
|
|
663e2d3b08 | ||
|
|
858b86af6a | ||
|
|
647300fd3a | ||
|
|
3eaf99390e | ||
|
|
0ee7915e31 | ||
|
|
10c880a800 | ||
|
|
b1bf658ff3 | ||
|
|
77dc13d9ba | ||
|
|
21b6cfc013 | ||
|
|
5d953ee23e | ||
|
|
42f2f5bfee | ||
|
|
6708901394 | ||
|
|
788038beb7 | ||
|
|
18ac05ed80 | ||
|
|
02f68821d2 | ||
|
|
62886f00f1 | ||
|
|
f2eb45d00b | ||
|
|
2fca9097c2 | ||
|
|
051677e7dd | ||
|
|
fbcd595b45 | ||
|
|
aa38f58f62 | ||
|
|
a3b01a02ee | ||
|
|
8544983984 | ||
|
|
07a11799b1 | ||
|
|
65a7b1933a | ||
|
|
bbfb735124 | ||
|
|
7b474fb0e1 | ||
|
|
c6524d7cee | ||
|
|
148cdf5109 | ||
|
|
60a3039415 | ||
|
|
6f7b6e30c9 | ||
|
|
08c095eae4 | ||
|
|
9087be6282 | ||
|
|
d46fe35eab | ||
|
|
1afb77ee33 | ||
|
|
a9215fc11c | ||
|
|
d80a110280 | ||
|
|
fdfc7f44fb | ||
|
|
26fbfc08f7 | ||
|
|
89049ee3ab | ||
|
|
68f81f900b | ||
|
|
513bd891af | ||
|
|
37cd753ab8 | ||
|
|
34bf2311b4 | ||
|
|
a86093f617 | ||
|
|
71f66e6004 | ||
|
|
2d1ab85e01 | ||
|
|
f029eb9102 | ||
|
|
471837ab88 | ||
|
|
1e80d742cc | ||
|
|
16b6273938 | ||
|
|
3407370563 | ||
|
|
3cda73a36c | ||
|
|
b604c423ad | ||
|
|
0c8b121c2c | ||
|
|
da11961611 | ||
|
|
48b9952fc0 | ||
|
|
99fa99eba5 | ||
|
|
06e67e04ac | ||
|
|
4a2f7c20a2 | ||
|
|
82e8769407 | ||
|
|
ad38702df1 | ||
|
|
3f98c62a8f | ||
|
|
ec7447992f | ||
|
|
99958e13eb | ||
|
|
b9fe5d4a14 | ||
|
|
1443fd5714 | ||
|
|
798083b235 | ||
|
|
18a7311089 | ||
|
|
9f49d62f12 | ||
|
|
0c3c4e7ea1 | ||
|
|
7cdf653842 | ||
|
|
32d99eada8 | ||
|
|
9e4adb16c6 | ||
|
|
4b5638e4f0 | ||
|
|
d8fb9cdd30 | ||
|
|
3920489dcd | ||
|
|
09d013588d | ||
|
|
d8bd03c65d | ||
|
|
8b2c6b9498 | ||
|
|
0f63d3a0d4 | ||
|
|
672eb333b0 | ||
|
|
de609b4d05 | ||
|
|
7e6e9eee36 | ||
|
|
a321bb733c | ||
|
|
490369a3e1 | ||
|
|
a2cce044ef | ||
|
|
8884bb88c7 | ||
|
|
313b0e9131 | ||
|
|
47048cf7e7 | ||
|
|
ef1c4615aa | ||
|
|
c9587b2bf0 | ||
|
|
669fa2fd22 | ||
|
|
fb8ec72825 | ||
|
|
d1687f574f | ||
|
|
72f47bec1f | ||
|
|
20e8aecf83 | ||
|
|
ee41474670 | ||
|
|
ac29e28eda | ||
|
|
58b96cbdee | ||
|
|
0c1fc15251 | ||
|
|
7b8e0cc952 | ||
|
|
c8aa591feb | ||
|
|
afa4d6e807 | ||
|
|
e6b7eda9e7 | ||
|
|
bbc4e05c8f | ||
|
|
50bccbaa2e | ||
|
|
d71e83a2af | ||
|
|
92a67702b7 | ||
|
|
2f9185efa7 | ||
|
|
a10216520b | ||
|
|
d5354e5156 | ||
|
|
a8f9b1f732 | ||
|
|
08a796e621 | ||
|
|
15f3a68f8e | ||
|
|
e0bc6dafa0 | ||
|
|
430c1236d7 | ||
|
|
3ac357161a | ||
|
|
3c5f1bf21f | ||
|
|
5544cd3770 | ||
|
|
71a060feb9 | ||
|
|
25d643257c | ||
|
|
85900e5bff | ||
|
|
d3257d6c5e | ||
|
|
afe1399f72 | ||
|
|
20ce2d3e22 | ||
|
|
cea8f2290d | ||
|
|
1b5b59c41b | ||
|
|
8df7c19e84 | ||
|
|
88b0069b94 | ||
|
|
3b8ddc9f06 | ||
|
|
708c7f38ec | ||
|
|
139daa1cc2 | ||
|
|
b85ac778c0 | ||
|
|
a3e2c9e61d | ||
|
|
ad78a0570c | ||
|
|
7e6ee274d8 | ||
|
|
daf7411c36 | ||
|
|
d3dedbe6cd | ||
|
|
d5411586d5 | ||
|
|
94e2d40554 | ||
|
|
db80fa1b46 | ||
|
|
9b569b8d07 | ||
|
|
90288880fb | ||
|
|
870e518ff6 | ||
|
|
6d89169bae | ||
|
|
ac77ee2703 | ||
|
|
18dcf603e9 | ||
|
|
71aaba1b86 | ||
|
|
64b9d5f17a | ||
|
|
a9f23c4bfe | ||
|
|
f454f46778 | ||
|
|
432936ea2c | ||
|
|
8211b0ac88 | ||
|
|
c314987e13 | ||
|
|
fc6cd74752 | ||
|
|
a47011e579 | ||
|
|
fbeeec6539 | ||
|
|
e1cd79cd9d | ||
|
|
692ced1ee7 | ||
|
|
c5cc127df4 | ||
|
|
5f00f10ae8 | ||
|
|
0fc9716f06 | ||
|
|
48fd3543e3 | ||
|
|
41985794c2 | ||
|
|
87a92fd001 | ||
|
|
f6f53f2a15 | ||
|
|
2531769b79 | ||
|
|
e9fa9f6cb8 | ||
|
|
b98ca86886 | ||
|
|
d917c8ffe6 | ||
|
|
5d44737199 | ||
|
|
6e8693a6b0 | ||
|
|
6d9e7404a1 | ||
|
|
70ceb389b5 | ||
|
|
832e364a2f | ||
|
|
51f9c5c7d8 | ||
|
|
15141c8528 | ||
|
|
0cdac10fd4 | ||
|
|
aaab548bfd | ||
|
|
46611b5606 | ||
|
|
fdbdb46577 | ||
|
|
e7fd81d0eb | ||
|
|
ce50bae6a0 | ||
|
|
8331cf3866 | ||
|
|
670262e53b | ||
|
|
c584f942cf | ||
|
|
e83b6bb583 | ||
|
|
cc5de668f0 | ||
|
|
653c7b8c4b | ||
|
|
4e38bcd4cc | ||
|
|
fcbeda4888 | ||
|
|
4101fddc5b | ||
|
|
86df7bea22 | ||
|
|
4db8c0c286 | ||
|
|
1d03335903 | ||
|
|
b1b541014f | ||
|
|
b40a7e435f | ||
|
|
8305fb4c5f | ||
|
|
6c678be511 | ||
|
|
2823663f09 | ||
|
|
033c2264f8 | ||
|
|
8eb6ce19f7 | ||
|
|
1cfc4e6f65 | ||
|
|
454d76e305 | ||
|
|
f3609fc3a5 | ||
|
|
16da51d095 | ||
|
|
51fb4c7a29 | ||
|
|
0b27cb55dc | ||
|
|
9e836eef69 | ||
|
|
07be4b8258 | ||
|
|
217087c6d4 | ||
|
|
2315a18c86 | ||
|
|
f59e9d2dc5 | ||
|
|
1da220085c | ||
|
|
962b2a01f1 | ||
|
|
eeda14f424 | ||
|
|
6269e20085 | ||
|
|
3d05296eb2 | ||
|
|
e90ab95cd0 | ||
|
|
23ec2f025e | ||
|
|
624898dd5e | ||
|
|
c6d6604399 | ||
|
|
7473704954 | ||
|
|
7a6b216bf6 | ||
|
|
1ef09124af | ||
|
|
6340f73d61 | ||
|
|
b9f4992a33 | ||
|
|
07a1fee013 | ||
|
|
63f37e51ad | ||
|
|
01ea08a5cb | ||
|
|
bddb20c5bb | ||
|
|
f9a97297da | ||
|
|
5fc2b56635 | ||
|
|
172953ccb3 | ||
|
|
d55300f53d | ||
|
|
c3d4500e3a | ||
|
|
cf72e28727 | ||
|
|
b488a1be16 | ||
|
|
805d0cb4b6 | ||
|
|
fa045c2504 | ||
|
|
23fd373a9b | ||
|
|
ab619cb073 | ||
|
|
2d96822014 | ||
|
|
887adc87de | ||
|
|
4219bd02c5 | ||
|
|
48544464ae | ||
|
|
9b4f2477e4 | ||
|
|
26e8840484 | ||
|
|
b6ef6d3ce9 | ||
|
|
cdfcd24049 | ||
|
|
95a8e0b125 | ||
|
|
84b7509f33 | ||
|
|
a53baec9dc | ||
|
|
4b4bc86cf7 | ||
|
|
4d3860089e | ||
|
|
758f55f13c | ||
|
|
1e191c3adc | ||
|
|
1ba2cf7c9d | ||
|
|
9ed1cc00d5 | ||
|
|
3be1173197 | ||
|
|
f6e69c62b1 | ||
|
|
6d9679a7fc | ||
|
|
6ea260a81f | ||
|
|
26da600761 | ||
|
|
33172a7aee | ||
|
|
7eb9f6ac95 | ||
|
|
fc06c285d5 | ||
|
|
ca2e4fec79 | ||
|
|
87ca90f6cc | ||
|
|
7dfea78c87 | ||
|
|
d7808009dc | ||
|
|
62afb7a130 | ||
|
|
87bef9cb9c | ||
|
|
3860cb21c6 | ||
|
|
bf4ad7886c | ||
|
|
1d51c207f4 | ||
|
|
2eb3f70e0c | ||
|
|
eb4d883194 | ||
|
|
3f5c9ff9de | ||
|
|
47039796de | ||
|
|
fd054abcca | ||
|
|
2861bfbd47 | ||
|
|
6ea3127f9c | ||
|
|
f7acb5e115 | ||
|
|
b1ad1f7699 | ||
|
|
ab713df3c0 | ||
|
|
e0d44f2f1c | ||
|
|
61463bc05f | ||
|
|
a219fc3527 | ||
|
|
1cd4028ce8 | ||
|
|
de9a30eb93 | ||
|
|
2210561af3 | ||
|
|
c5ddc474c4 | ||
|
|
688748da36 | ||
|
|
753d311593 | ||
|
|
5bae3d9931 | ||
|
|
a3495a8218 | ||
|
|
5fa68f77a2 | ||
|
|
dbba2e9791 | ||
|
|
6207f26a68 | ||
|
|
7fcd39822e | ||
|
|
d20f8a604f | ||
|
|
a742646127 | ||
|
|
f5078d5ef2 | ||
|
|
3784084abc | ||
|
|
2f463b7735 | ||
|
|
7be5625d66 | ||
|
|
aef14a9cd2 | ||
|
|
67648a04d3 | ||
|
|
58345e3194 | ||
|
|
4cb2ad9a5a | ||
|
|
27395d8e22 | ||
|
|
230c5ea180 | ||
|
|
4d4370ddce | ||
|
|
7e913e7847 | ||
|
|
ec7871e4e9 | ||
|
|
cc5a45a14f | ||
|
|
5fff3457ba | ||
|
|
6f5d66c1d9 | ||
|
|
d94795bf4f | ||
|
|
009b71c42a | ||
|
|
a10716d74e | ||
|
|
b44bf5f401 | ||
|
|
e0af1838e2 | ||
|
|
7d5502a34f |
@@ -0,0 +1 @@
|
|||||||
|
worktrees/
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# CAPod - Companion for AirPods
|
||||||
|
|
||||||
|
Android app that detects and monitors AirPods via Bluetooth LE. Displays battery levels, triggers popup notifications on case open, and provides home screen widgets.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
| Module | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `app/` | Main Android app (FOSS and Google Play flavors) |
|
||||||
|
| `app-common/` | Shared code between phone and Wear OS apps |
|
||||||
|
|
||||||
|
## Build Flavors
|
||||||
|
|
||||||
|
- **FOSS** (`foss`): Open-source, no Google Play dependencies
|
||||||
|
- **Google Play** (`gplay`): Includes billing client for IAP
|
||||||
|
|
||||||
|
Quick build check: `./gradlew assembleFossDebug`
|
||||||
|
|
||||||
|
## Key Locations
|
||||||
|
|
||||||
|
| Path | Contains |
|
||||||
|
|------|----------|
|
||||||
|
| `app/src/main/java/` | Main app source (activities, fragments, services) |
|
||||||
|
| `app-common/src/main/java/` | Shared logic (monitor, bluetooth, models) |
|
||||||
|
| `app/src/main/res/` | Layouts, drawables, strings |
|
||||||
|
| `app-common/src/test/` | Unit tests |
|
||||||
|
| `app/build.gradle.kts` | App build config, dependencies, flavors |
|
||||||
|
| `app/src/debug/java/.../screenshots/` | Play Store screenshot content composables |
|
||||||
|
| `fastlane/` | Screenshot generation scripts, Play Store metadata |
|
||||||
|
|
||||||
|
## Development Tips
|
||||||
|
|
||||||
|
- Use `assembleFossDebug` as the fastest build variant for iteration
|
||||||
|
- Shared code goes in `app-common/`, app-specific code in `app/`
|
||||||
|
- Follow existing patterns — the codebase uses MVVM + Hilt + Coroutines
|
||||||
|
- Always use string resources for user-facing text (see localization rules)
|
||||||
|
- Check `git log --oneline -20` for commit message style before committing
|
||||||
|
|
||||||
|
## Rules Reference
|
||||||
|
|
||||||
|
Detailed guidelines are in `.claude/rules/`:
|
||||||
|
|
||||||
|
- `architecture.md` — Module structure, key components, data flow, dependencies
|
||||||
|
- `build-commands.md` — Build, test, lint, and release commands
|
||||||
|
- `localization.md` — String resource naming conventions
|
||||||
|
- `commit-guidelines.md` — Commit message format and prefixes
|
||||||
|
- `pull-requests.md` — PR title and description conventions
|
||||||
|
- `agent-instructions.md` — Sub-agent delegation and critical thinking
|
||||||
|
- `screenshots.md` — Play Store screenshot pipeline, commands, adding new screens
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
description: Instructions for Claude Code sub-agents and task delegation
|
||||||
|
globs:
|
||||||
|
- "**"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Agent Instructions
|
||||||
|
|
||||||
|
## Critical Thinking
|
||||||
|
|
||||||
|
- Do not blindly accept information at face value
|
||||||
|
- Verify assumptions against actual code before proceeding
|
||||||
|
- When encountering unexpected behavior, investigate root causes rather than applying workarounds
|
||||||
|
- If something seems wrong, it probably is — dig deeper
|
||||||
|
|
||||||
|
## Explore vs. Implement
|
||||||
|
|
||||||
|
- **Explore first**: Before making changes, understand the existing code structure and patterns
|
||||||
|
- **Read before writing**: Always read relevant files before modifying them
|
||||||
|
- **Follow existing patterns**: Match the code style and architecture already in use
|
||||||
|
- **Minimal changes**: Only change what's necessary to accomplish the task
|
||||||
|
|
||||||
|
## Sub-Agent Delegation
|
||||||
|
|
||||||
|
When using Task tool to spawn sub-agents:
|
||||||
|
|
||||||
|
- Provide complete context — sub-agents don't share your conversation history unless noted
|
||||||
|
- Be specific about what you need: research only, or research + implementation
|
||||||
|
- Use `Explore` agent type for codebase investigation
|
||||||
|
- Use `Bash` agent type for running builds and tests
|
||||||
|
- Parallelize independent sub-agent tasks for efficiency
|
||||||
|
|
||||||
|
## Common Pitfalls
|
||||||
|
|
||||||
|
- Don't create new files when editing existing ones would suffice
|
||||||
|
- Don't add features beyond what was requested
|
||||||
|
- Don't refactor surrounding code when fixing a bug
|
||||||
|
- Don't add comments or documentation to code you didn't change
|
||||||
|
- Don't guess at file paths — use Glob/Grep to find them
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
---
|
||||||
|
description: Architecture overview, module structure, key components, data flow, and dependencies
|
||||||
|
globs:
|
||||||
|
- "app/**/*.kt"
|
||||||
|
- "app-common/**/*.kt"
|
||||||
|
- "**/*.gradle.kts"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Architecture
|
||||||
|
|
||||||
|
## Multi-Module Structure
|
||||||
|
|
||||||
|
- **app/**: Main Android application with FOSS and Google Play flavors
|
||||||
|
- **app-common/**: Shared code between main app and Wear OS app
|
||||||
|
|
||||||
|
## Core Patterns
|
||||||
|
|
||||||
|
- **MVVM**: ViewModels with LiveData/StateFlow for UI state management
|
||||||
|
- **Dependency Injection**: Hilt/Dagger for dependency management
|
||||||
|
- **Coroutines**: Kotlin coroutines for async operations
|
||||||
|
- **Repository Pattern**: Data layer abstraction for monitoring and settings
|
||||||
|
|
||||||
|
## Key Components
|
||||||
|
|
||||||
|
### PodMonitor System
|
||||||
|
|
||||||
|
- `PodMonitor`: Core service that detects and tracks AirPods via Bluetooth LE
|
||||||
|
- `MonitorControl`: Manages MonitorService lifecycle
|
||||||
|
- `MonitorService`: Foreground service that continuously scans for AirPods
|
||||||
|
- `BluetoothEventReceiver`: Handles system Bluetooth events
|
||||||
|
|
||||||
|
### Reaction System
|
||||||
|
|
||||||
|
- `ReactionSettingsFragment`: Configuration for popup notifications
|
||||||
|
- `PopUpWindow`: Displays AirPods status when case is opened
|
||||||
|
- `PopUpPodViewFactory`: Creates UI components for different pod models
|
||||||
|
|
||||||
|
### Common Utilities
|
||||||
|
|
||||||
|
- `EdgeToEdgeHelper`: Handles Android edge-to-edge display insets
|
||||||
|
|
||||||
|
## Build Configuration
|
||||||
|
|
||||||
|
### Flavors
|
||||||
|
|
||||||
|
- **FOSS**: Open-source version without Google Play dependencies
|
||||||
|
- **Google Play (gplay)**: Version with billing client for in-app purchases
|
||||||
|
|
||||||
|
### Build Types
|
||||||
|
|
||||||
|
- **debug**: Unobfuscated, full logging, no minification
|
||||||
|
- **beta**: Obfuscated, production-ready with strict lint checks
|
||||||
|
- **release**: Fully optimized for production distribution
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
The app follows a unidirectional data flow:
|
||||||
|
|
||||||
|
1. `BluetoothEventReceiver` detects Bluetooth events
|
||||||
|
2. `MonitorService` scans for AirPods beacon data
|
||||||
|
3. `PodMonitor` processes and stores device information
|
||||||
|
4. ViewModels observe monitor data via repositories
|
||||||
|
5. UI components react to ViewModel state changes
|
||||||
|
6. `ReactionSystem` triggers popups and notifications
|
||||||
|
|
||||||
|
## Bluetooth LE Implementation
|
||||||
|
|
||||||
|
The app uses Android's Bluetooth LE APIs to scan for Apple device advertisements. The core scanning logic is in `MonitorService` which runs as a foreground service.
|
||||||
|
|
||||||
|
## Multi-Platform Considerations
|
||||||
|
|
||||||
|
Code shared between phone and Wear OS apps is placed in `app-common`. When modifying shared functionality, ensure compatibility across both platforms.
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
- **Unit Tests**: Located in `app-common/src/test/` for shared logic
|
||||||
|
- **Test Flavors**: Separate test configurations for FOSS and Google Play variants
|
||||||
|
|
||||||
|
## Key Dependencies
|
||||||
|
|
||||||
|
- **Hilt**: Dependency injection framework
|
||||||
|
- **AndroidX Navigation**: Fragment navigation with SafeArgs
|
||||||
|
- **kotlinx.serialization**: JSON serialization for configuration and caching
|
||||||
|
- **Material Design**: UI components following Material Design guidelines
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
description: Build, test, lint, and release commands for Gradle
|
||||||
|
globs:
|
||||||
|
- "**/*.gradle.kts"
|
||||||
|
- "**/*.gradle"
|
||||||
|
- "gradle/**"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Build Commands
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build debug version
|
||||||
|
./gradlew assembleDebug
|
||||||
|
|
||||||
|
# Build all variants (FOSS and Google Play flavors)
|
||||||
|
./gradlew assemble
|
||||||
|
|
||||||
|
# Build specific flavor and type
|
||||||
|
./gradlew assembleFossDebug
|
||||||
|
./gradlew assembleGplayRelease
|
||||||
|
|
||||||
|
# Build app bundles for Play Store
|
||||||
|
./gradlew bundleGplayRelease
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all unit tests
|
||||||
|
./gradlew test
|
||||||
|
|
||||||
|
# Run unit tests for specific variant
|
||||||
|
./gradlew testFossDebugUnitTest
|
||||||
|
|
||||||
|
# Run instrumentation tests (requires connected device/emulator)
|
||||||
|
./gradlew connectedAndroidTest
|
||||||
|
./gradlew connectedFossDebugAndroidTest
|
||||||
|
|
||||||
|
# Run all checks (lint + tests)
|
||||||
|
./gradlew check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Quality
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run lint for all variants
|
||||||
|
./gradlew lint
|
||||||
|
|
||||||
|
# Run lint for specific variant
|
||||||
|
./gradlew lintFossDebug
|
||||||
|
|
||||||
|
# Auto-fix lint issues where possible
|
||||||
|
./gradlew lintFix
|
||||||
|
|
||||||
|
# Update lint baseline
|
||||||
|
./gradlew updateLintBaseline
|
||||||
|
```
|
||||||
|
|
||||||
|
## Release
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew assembleFossRelease assembleGplayRelease
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Use `assembleFossDebug` as the default quick-check build (fastest variant)
|
||||||
|
- Run `./gradlew check` before submitting changes to catch lint and test issues
|
||||||
|
- Instrumentation tests require a connected device or running emulator
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
description: Git commit message format and conventions
|
||||||
|
globs:
|
||||||
|
- "**"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Commit Guidelines
|
||||||
|
|
||||||
|
## Format
|
||||||
|
|
||||||
|
```
|
||||||
|
<prefix>: <Short summary>
|
||||||
|
```
|
||||||
|
|
||||||
|
Summary line should be concise and describe the change. No period at the end.
|
||||||
|
|
||||||
|
## Prefixes
|
||||||
|
|
||||||
|
Use the existing commit history as reference. Common prefixes:
|
||||||
|
|
||||||
|
- **fix**: Bug fixes (e.g., `fix: Handle display cutouts in landscape mode`)
|
||||||
|
- **feat**: New features
|
||||||
|
- **refactor**: Code restructuring without behavior change
|
||||||
|
- **chore**: Maintenance, dependency updates, build config
|
||||||
|
- **docs**: Documentation changes
|
||||||
|
|
||||||
|
## Component Scope (optional)
|
||||||
|
|
||||||
|
When a change is scoped to a specific area, include it after the prefix:
|
||||||
|
|
||||||
|
- `fix(widget): Fix layout for devices with single charge detection`
|
||||||
|
- `feat(monitor): Add battery level caching`
|
||||||
|
- `refactor(popup): Extract pod view factory`
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- Keep the summary line under 72 characters
|
||||||
|
- Use imperative mood ("Add feature" not "Added feature")
|
||||||
|
- Reference issue numbers when applicable
|
||||||
|
- Do not include `Co-authored-by` trailers
|
||||||
|
- Look at recent `git log` output to match the project's existing style
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
description: Guidelines for adding and naming Android string resources
|
||||||
|
globs:
|
||||||
|
- "**/res/values*/strings.xml"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Localization Guidelines
|
||||||
|
|
||||||
|
When adding new user-facing strings:
|
||||||
|
|
||||||
|
- **Always use string resources**: Never hardcode user-facing text in layouts or code
|
||||||
|
- **Follow naming conventions**: Use descriptive, hierarchical naming (e.g., `profiles_name_default`, `settings_bluetooth_enabled`)
|
||||||
|
- **Provide context**: String names should indicate usage and location
|
||||||
|
- **Consider pluralization**: Use Android plural resources (`<plurals>`) when quantities vary
|
||||||
|
|
||||||
|
## Naming Examples
|
||||||
|
|
||||||
|
- `profiles_create_title` (screen title)
|
||||||
|
- `profiles_name_label` (form field label)
|
||||||
|
- `profiles_delete_confirmation` (dialog message)
|
||||||
|
- `error_network_unavailable` (error message)
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
description: Pull request naming and description conventions
|
||||||
|
globs:
|
||||||
|
- "**"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Pull Request Guidelines
|
||||||
|
|
||||||
|
## PR Title Format
|
||||||
|
|
||||||
|
```
|
||||||
|
<Category>: <Short user-facing summary>
|
||||||
|
```
|
||||||
|
|
||||||
|
PR titles appear in auto-generated changelogs and are read by users. Use **ELI5, user-facing language** — no internal class names, library names, or implementation details.
|
||||||
|
|
||||||
|
## Category Prefixes
|
||||||
|
|
||||||
|
| Prefix | Covers |
|
||||||
|
|--------|--------|
|
||||||
|
| **Widget** | Home screen widget |
|
||||||
|
| **Reaction** | Case-open popup, auto-play/pause, notification triggers |
|
||||||
|
| **Device** | AirPods detection, compatibility, Bluetooth scanning, battery reading, device profiles |
|
||||||
|
| **General** | Dashboard, settings, notifications, themes, onboarding, support, app-wide UI |
|
||||||
|
| **Fix** | Bug fixes spanning multiple areas |
|
||||||
|
|
||||||
|
### Title Examples
|
||||||
|
|
||||||
|
- `Widget: Add color themes and transparency slider`
|
||||||
|
- `Reaction: Fix popup appearing twice when opening AirPods case`
|
||||||
|
- `Device: Add support for AirPods 4 with ANC`
|
||||||
|
- `General: Add dark mode and color theme settings`
|
||||||
|
- `Fix: Fix battery display stuck at 0% after reconnecting`
|
||||||
|
|
||||||
|
### Bad Titles (too technical)
|
||||||
|
|
||||||
|
- `refactor(settings): Migrate preferences to AndroidX DataStore`
|
||||||
|
- `feat(widget): Migrate to Jetpack Glance`
|
||||||
|
- `refactor(ui): Migrate from Fragments to Jetpack Compose`
|
||||||
|
|
||||||
|
## PR Description Format
|
||||||
|
|
||||||
|
### What changed
|
||||||
|
|
||||||
|
User-friendly explanation of what this PR does. Describe the problem that was fixed or the feature that was added from the user's perspective. No internal class or method names.
|
||||||
|
|
||||||
|
For non-user-facing PRs (refactors, tests, CI, dependency bumps): write "No user-facing behavior change" followed by a brief internal description.
|
||||||
|
|
||||||
|
### Technical Context
|
||||||
|
|
||||||
|
Explain what's hard to extract from the diff alone. Focus on:
|
||||||
|
|
||||||
|
- **Why** this approach was chosen (and alternatives considered/rejected)
|
||||||
|
- **Root cause** for bug fixes (the diff shows the fix, not what caused it)
|
||||||
|
- **Non-obvious side effects** or behavioral changes not apparent from reading the code
|
||||||
|
- **Review guidance** — what's tricky or deserves close attention
|
||||||
|
|
||||||
|
Keep it scannable with bullet points. Don't restate what's visible in the diff (file names, class renames, line-level changes).
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## What changed
|
||||||
|
|
||||||
|
Fixed a crash that could happen when the AirPods case is opened while Bluetooth is turning off.
|
||||||
|
|
||||||
|
## Technical Context
|
||||||
|
|
||||||
|
- Root cause: `MonitorService` continued processing scan results during Bluetooth adapter state change, hitting a null adapter reference
|
||||||
|
- Chose to gate on adapter state in the scan callback rather than adding a separate BroadcastReceiver, since the service already observes adapter state for restart logic
|
||||||
|
- The timing window is ~200ms between ACTION_STATE_CHANGING and ACTION_STATE_OFF — only reproducible on Pixel devices with aggressive Bluetooth power management
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- **Issue references**: Use "Closes #123", "Fixes #123", or "Resolves #123"
|
||||||
|
- **Breaking changes**: Mark with "BREAKING:" prefix if applicable
|
||||||
|
- **No `Co-authored-by` trailers** (per project convention)
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# Play Store Screenshot Pipeline
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Localized screenshots are generated using Compose Preview Screenshot Testing (alpha), rendered offline (no device needed), and sorted into fastlane metadata directories for Play Store upload.
|
||||||
|
|
||||||
|
## Pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
ScreenshotContent.kt (mock data + composables)
|
||||||
|
→ PlayStoreScreenshots.kt (@PreviewTest entry points)
|
||||||
|
→ PlayStoreLocales.kt (multi-preview locale annotations, auto-generated per batch)
|
||||||
|
→ generate_screenshots.sh (batched Gradle runs to avoid OOM)
|
||||||
|
→ copy_screenshots.sh (sort PNGs into fastlane locale dirs)
|
||||||
|
→ fastlane/metadata/android/{locale}/images/phoneScreenshots/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Files
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `app/src/debug/java/.../screenshots/ScreenshotContent.kt` | Mock data composables for each screen (8 screens) |
|
||||||
|
| `app/src/screenshotTest/kotlin/.../screenshots/PlayStoreScreenshots.kt` | `@PreviewTest` functions that wire content to locale annotations |
|
||||||
|
| `app/src/screenshotTest/kotlin/.../screenshots/PlayStoreLocales.kt` | Multi-preview annotations (auto-generated by batch script) |
|
||||||
|
| `fastlane/generate_screenshots.sh` | Batched generation across 68 locales |
|
||||||
|
| `fastlane/copy_screenshots.sh` | Copies rendered PNGs into fastlane structure |
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Full run — all 68 locales, ~12 batches, ~7 minutes
|
||||||
|
./fastlane/generate_screenshots.sh
|
||||||
|
|
||||||
|
# Smoke test — 6 locales (en, de, ja, ar, zh-CN, pt-BR), single batch
|
||||||
|
./fastlane/generate_screenshots.sh --smoke
|
||||||
|
|
||||||
|
# Copy into fastlane directories (run after generate)
|
||||||
|
./fastlane/copy_screenshots.sh
|
||||||
|
|
||||||
|
# Clean copy (removes old screenshots first)
|
||||||
|
./fastlane/copy_screenshots.sh --clean
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding a New Screenshot
|
||||||
|
|
||||||
|
1. Add a composable content function in `ScreenshotContent.kt` (e.g. `NewScreenContent()`)
|
||||||
|
2. Add a `@PreviewTest` function in `PlayStoreScreenshots.kt` that calls it
|
||||||
|
3. Add the function name → filename mapping in `copy_screenshots.sh` `SCREEN_MAP`
|
||||||
|
4. Update the expected count in `generate_screenshots.sh` (composables per locale)
|
||||||
|
5. Run the full pipeline: `generate_screenshots.sh` then `copy_screenshots.sh`
|
||||||
|
|
||||||
|
## After UI Changes
|
||||||
|
|
||||||
|
When modifying a screen that appears in screenshots (check `ScreenshotContent.kt`), regenerate:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./fastlane/generate_screenshots.sh
|
||||||
|
./fastlane/copy_screenshots.sh --clean
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical Notes
|
||||||
|
|
||||||
|
- Batch size defaults to 2 locales (16 renders) to avoid layoutlib memory leak (~10MB/image)
|
||||||
|
- Gradle daemon is stopped between batches to release memory
|
||||||
|
- `PlayStoreLocales.kt` is temporarily rewritten per batch and restored via trap
|
||||||
|
- Device spec: 1080x2400px @ 428 DPI (Pixel-class phone)
|
||||||
|
- Uses `com.android.compose.screenshot` plugin v0.0.1-alpha13
|
||||||
|
- Output: `app/src/screenshotTestGplayDebug/reference/`
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"mcp__ide__getDiagnostics",
|
||||||
|
"WebSearch"
|
||||||
|
],
|
||||||
|
"deny": []
|
||||||
|
},
|
||||||
|
"enabledPlugins": {
|
||||||
|
"android-translation@claude-code-cafe": true,
|
||||||
|
"debugbadger@claude-code-cafe": true,
|
||||||
|
"jvm-tools@claude-code-cafe": true,
|
||||||
|
"frontend-design@claude-plugins-official": true,
|
||||||
|
"kotlin-lsp@claude-plugins-official": true
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
github:
|
||||||
|
- d4rken
|
||||||
custom:
|
custom:
|
||||||
- "https://www.buymeacoffee.com/tydarken"
|
- "https://www.buymeacoffee.com/tydarken"
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
name: 'Common project setup'
|
||||||
|
description: 'Setup the base project'
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Set up JDK 17
|
||||||
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 #v5.2.0
|
||||||
|
with:
|
||||||
|
java-version: '17'
|
||||||
|
distribution: 'temurin'
|
||||||
|
|
||||||
|
- name: Grant execute permission for gradlew
|
||||||
|
shell: bash
|
||||||
|
run: chmod +x gradlew
|
||||||
|
|
||||||
|
- name: Cache Gradle Wrapper
|
||||||
|
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 #v5.0.3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/wrapper
|
||||||
|
!~/.gradle/wrapper/dists/**/gradle*.zip
|
||||||
|
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('**/gradle-wrapper.properties') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-wrapper-
|
||||||
|
|
||||||
|
- name: Cache Gradle Dependencies
|
||||||
|
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 #v5.0.3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
key: ${{ runner.os }}-gradle-caches-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'buildSrc/**/*.kt') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-caches-
|
||||||
|
|
||||||
|
- name: Cache Android Global Build-Cache
|
||||||
|
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 #v5.0.3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.android/build-cache
|
||||||
|
key: ${{ runner.os }}-android-build-cache-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-android-build-cache-
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
changelog:
|
||||||
|
exclude:
|
||||||
|
labels:
|
||||||
|
- changelog-ignore
|
||||||
|
categories:
|
||||||
|
- title: ":rocket: Enhancements"
|
||||||
|
labels:
|
||||||
|
- enhancement
|
||||||
|
- title: ":lady_beetle: Bug fixes"
|
||||||
|
labels:
|
||||||
|
- bug
|
||||||
|
- title: ":shrug: Other changes"
|
||||||
|
labels:
|
||||||
|
- "*"
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
name: Android CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build and test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: set up JDK 11
|
|
||||||
uses: actions/setup-java@v2
|
|
||||||
with:
|
|
||||||
java-version: '11'
|
|
||||||
distribution: 'adopt'
|
|
||||||
cache: gradle
|
|
||||||
|
|
||||||
- name: Grant execute permission for gradlew
|
|
||||||
run: chmod +x gradlew
|
|
||||||
- name: Build with Gradle
|
|
||||||
run: ./gradlew assembleDebug
|
|
||||||
- name: Run tests
|
|
||||||
run: ./gradlew testGplayDebugUnitTest testFossDebugUnitTest
|
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
name: Code tests & eval
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint-vital:
|
||||||
|
name: Lint vitals
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
flavor: [ Foss,Gplay ]
|
||||||
|
variant: [ Beta,Release ]
|
||||||
|
module: [ app ]
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||||
|
- name: Setup project and build environment
|
||||||
|
uses: ./.github/actions/common-setup
|
||||||
|
|
||||||
|
- name: Check for fatal lint issues
|
||||||
|
run: ./gradlew lintVital${{ matrix.flavor }}${{ matrix.variant }}
|
||||||
|
|
||||||
|
build-modules:
|
||||||
|
name: Build apps
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
flavor: [ Foss,Gplay ]
|
||||||
|
variant: [ Debug ]
|
||||||
|
module: [ app ]
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||||
|
- name: Setup project and build environment
|
||||||
|
uses: ./.github/actions/common-setup
|
||||||
|
|
||||||
|
- name: Build modules
|
||||||
|
run: ./gradlew ${{ matrix.module }}:assemble${{ matrix.flavor }}${{ matrix.variant }}
|
||||||
|
|
||||||
|
test-modules:
|
||||||
|
name: Unit tests
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
variant: [ Debug ]
|
||||||
|
flavor: [ testFoss,testGplay ]
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||||
|
- name: Setup project and build environment
|
||||||
|
uses: ./.github/actions/common-setup
|
||||||
|
|
||||||
|
- name: Test modules
|
||||||
|
run: ./gradlew ${{ matrix.flavor }}${{ matrix.variant }}UnitTest
|
||||||
|
|
||||||
|
check-fastlane-metadata:
|
||||||
|
name: Fastlane metadata
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||||
|
- name: Validate metadata lengths
|
||||||
|
run: bash fastlane/check_metadata_length.sh
|
||||||
@@ -11,7 +11,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
validation:
|
validation:
|
||||||
name: "Validation"
|
name: "Validation"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/actions/wrapper-validation@0723195856401067f7a2779048b490ace7a47d7c #v5.0.2
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
name: Tagged releases
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
dry_run:
|
||||||
|
description: 'Build only, skip release/upload'
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-github:
|
||||||
|
name: Create GitHub release
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
environment: foss-production
|
||||||
|
steps:
|
||||||
|
- name: Decode Keystore
|
||||||
|
env:
|
||||||
|
ENCODED_KEYSTORE: ${{ secrets.SIGNING_KEYSTORE_BASE64 }}
|
||||||
|
run: |
|
||||||
|
TMP_KEYSTORE_DIR_PATH="${RUNNER_TEMP}"/keystore
|
||||||
|
mkdir -p "${TMP_KEYSTORE_DIR_PATH}"
|
||||||
|
TMP_KEYSTORE_FILE_PATH="${TMP_KEYSTORE_DIR_PATH}"/keystore.jks
|
||||||
|
echo $ENCODED_KEYSTORE | base64 -di > "${TMP_KEYSTORE_FILE_PATH}"
|
||||||
|
echo "STORE_PATH=$(echo $TMP_KEYSTORE_FILE_PATH)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup project and build environment
|
||||||
|
uses: ./.github/actions/common-setup
|
||||||
|
|
||||||
|
- name: Assemble beta APK
|
||||||
|
if: contains(github.ref_name, '-beta')
|
||||||
|
run: ./gradlew assembleFossBeta
|
||||||
|
env:
|
||||||
|
VERSION: ${{ github.ref }}
|
||||||
|
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
|
||||||
|
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
||||||
|
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Assemble production APK
|
||||||
|
if: "!contains(github.ref_name, '-beta')"
|
||||||
|
run: ./gradlew assembleFossRelease
|
||||||
|
env:
|
||||||
|
VERSION: ${{ github.ref }}
|
||||||
|
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
|
||||||
|
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
||||||
|
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Create pre-release
|
||||||
|
if: contains(github.ref_name, '-beta') && !(github.event_name == 'workflow_dispatch' && inputs.dry_run)
|
||||||
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b #v2.5.0
|
||||||
|
with:
|
||||||
|
prerelease: true
|
||||||
|
tag_name: ${{ github.ref_name }}
|
||||||
|
name: ${{ github.ref_name }}
|
||||||
|
generate_release_notes: true
|
||||||
|
files: app/build/outputs/apk/foss/beta/eu.darken.capod-*.apk
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
if: "!contains(github.ref_name, '-beta') && !(github.event_name == 'workflow_dispatch' && inputs.dry_run)"
|
||||||
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b #v2.5.0
|
||||||
|
with:
|
||||||
|
prerelease: false
|
||||||
|
tag_name: ${{ github.ref_name }}
|
||||||
|
name: ${{ github.ref_name }}
|
||||||
|
generate_release_notes: true
|
||||||
|
files: app/build/outputs/apk/foss/release/eu.darken.capod-*.apk
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
release-gplay:
|
||||||
|
name: Create Google Play release
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
environment: gplay-production
|
||||||
|
steps:
|
||||||
|
- name: Decode Keystore
|
||||||
|
env:
|
||||||
|
ENCODED_KEYSTORE: ${{ secrets.SIGNING_KEYSTORE_BASE64 }}
|
||||||
|
run: |
|
||||||
|
TMP_KEYSTORE_DIR_PATH="${RUNNER_TEMP}"/keystore
|
||||||
|
mkdir -p "${TMP_KEYSTORE_DIR_PATH}"
|
||||||
|
TMP_KEYSTORE_FILE_PATH="${TMP_KEYSTORE_DIR_PATH}"/keystore.jks
|
||||||
|
echo $ENCODED_KEYSTORE | base64 -di > "${TMP_KEYSTORE_FILE_PATH}"
|
||||||
|
echo "STORE_PATH=$(echo $TMP_KEYSTORE_FILE_PATH)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Decode Google Play service account key
|
||||||
|
env:
|
||||||
|
ENCODED_SERVICE_KEY: ${{ secrets.GPLAY_SERVICE_ACCOUNT_KEY_JSON_BASE64 }}
|
||||||
|
run: |
|
||||||
|
TMP_SERVICEKEY_DIR="${RUNNER_TEMP}"/gplaykey
|
||||||
|
mkdir -p "${TMP_SERVICEKEY_DIR}"
|
||||||
|
TMP_SERVICEKEY_FILE_PATH="${TMP_SERVICEKEY_DIR}"/service_account.json
|
||||||
|
echo $ENCODED_SERVICE_KEY | base64 -di > "${TMP_SERVICEKEY_FILE_PATH}"
|
||||||
|
echo "SUPPLY_JSON_KEY=$(echo $TMP_SERVICEKEY_FILE_PATH)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup project and build environment
|
||||||
|
uses: ./.github/actions/common-setup
|
||||||
|
|
||||||
|
- name: Set up ruby env
|
||||||
|
uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 #v1.292.0
|
||||||
|
with:
|
||||||
|
ruby-version: 3.3.6
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Assemble beta and upload to Google Play
|
||||||
|
if: contains(github.ref_name, '-beta') && !(github.event_name == 'workflow_dispatch' && inputs.dry_run)
|
||||||
|
run: bundle exec fastlane beta
|
||||||
|
env:
|
||||||
|
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
|
||||||
|
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
||||||
|
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Assemble production and upload to Google Play
|
||||||
|
if: "!contains(github.ref_name, '-beta') && !(github.event_name == 'workflow_dispatch' && inputs.dry_run)"
|
||||||
|
run: bundle exec fastlane production
|
||||||
|
env:
|
||||||
|
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
|
||||||
|
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
||||||
|
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
||||||
+17
-2
@@ -1,4 +1,19 @@
|
|||||||
.idea
|
*.iml
|
||||||
.gradle
|
.gradle
|
||||||
build/
|
/local.properties
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
/.idea/**/*
|
||||||
|
!/.idea/codeStyles/
|
||||||
|
!/.idea/codeStyles/**/*
|
||||||
|
*.jks
|
||||||
|
.local/*
|
||||||
/fastlane/report.xml
|
/fastlane/report.xml
|
||||||
|
/fastlane/Appfile
|
||||||
|
/fastlane/README.md
|
||||||
|
.kotlin
|
||||||
|
# Screenshot test reference images (ephemeral, regenerated on demand)
|
||||||
|
app/src/screenshotTest*/reference/
|
||||||
Generated
+168
@@ -0,0 +1,168 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<option name="RIGHT_MARGIN" value="120" />
|
||||||
|
<JSON>
|
||||||
|
<option name="OBJECT_WRAPPING" value="1" />
|
||||||
|
<option name="ARRAY_WRAPPING" value="1" />
|
||||||
|
</JSON>
|
||||||
|
<JavaCodeStyleSettings>
|
||||||
|
<option name="DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION" value="true" />
|
||||||
|
<option name="IMPORT_LAYOUT_TABLE">
|
||||||
|
<value>
|
||||||
|
<package name="" withSubpackages="true" static="false" module="true" />
|
||||||
|
<package name="android" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="com" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="junit" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="net" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="org" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="java" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="javax" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="" withSubpackages="true" static="true" />
|
||||||
|
<emptyLine />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</JavaCodeStyleSettings>
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</JetCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="JAVA">
|
||||||
|
<option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
|
||||||
|
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
||||||
|
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" />
|
||||||
|
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
|
||||||
|
<option name="FIELD_ANNOTATION_WRAP" value="1" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="JSON">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="XML">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
|
</indentOptions>
|
||||||
|
<arrangement>
|
||||||
|
<rules>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:android</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:id</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>style</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
</rules>
|
||||||
|
</arrangement>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
<option name="METHOD_ANNOTATION_WRAP" value="1" />
|
||||||
|
<option name="CLASS_ANNOTATION_WRAP" value="1" />
|
||||||
|
<option name="FIELD_ANNOTATION_WRAP" value="1" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
||||||
Generated
+5
@@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
layout: plain
|
||||||
|
permalink: /changelog
|
||||||
|
title: "Changelog"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Changelog for CAPod
|
||||||
|
|
||||||
|
{% for release in site.github.releases %}
|
||||||
|
|
||||||
|
## {{ release.tag_name }} - {{ release.published_at | date: "%B %d, %Y" }}
|
||||||
|
|
||||||
|
{% assign clean_body = release.body | strip %}
|
||||||
|
{% assign no_comments = clean_body | replace: "<!-- Release notes generated using configuration in .github/release.yml", "" %}
|
||||||
|
{% assign no_comments = no_comments | split: "-->" %}
|
||||||
|
{% if no_comments.size > 1 %}
|
||||||
|
{% assign clean_content = no_comments[1] | strip %}
|
||||||
|
{% else %}
|
||||||
|
{% assign clean_content = no_comments[0] | strip %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% comment %} Make links clickable {% endcomment %}
|
||||||
|
{% assign lines = clean_content | split: "
|
||||||
|
" %}
|
||||||
|
{% assign processed_lines = "" %}
|
||||||
|
{% for line in lines %}
|
||||||
|
{% if line contains "**Full Changelog**:" %}
|
||||||
|
{% comment %} Handle Full Changelog links {% endcomment %}
|
||||||
|
{% assign parts = line | split: ": " %}
|
||||||
|
{% if parts.size > 1 %}
|
||||||
|
{% assign url = parts[1] | strip %}
|
||||||
|
{% assign clickable_line = "**[View Changes](" | append: url | append: ")**" %}
|
||||||
|
{% assign processed_lines = processed_lines | append: clickable_line | append: "
|
||||||
|
" %}
|
||||||
|
{% else %}
|
||||||
|
{% assign processed_lines = processed_lines | append: line | append: "
|
||||||
|
" %}
|
||||||
|
{% endif %}
|
||||||
|
{% elsif line contains " in https://github.com/" and line contains "/pull/" %}
|
||||||
|
{% comment %} Handle pull request links {% endcomment %}
|
||||||
|
{% assign pr_parts = line | split: " in https://github.com/" %}
|
||||||
|
{% if pr_parts.size > 1 %}
|
||||||
|
{% assign before_url = pr_parts[0] %}
|
||||||
|
{% assign after_url = pr_parts[1] %}
|
||||||
|
{% assign url = "https://github.com/" | append: after_url %}
|
||||||
|
{% assign pr_number = after_url | split: "/pull/" %}
|
||||||
|
{% if pr_number.size > 1 %}
|
||||||
|
{% assign pr_num = pr_number[1] | split: " " | first %}
|
||||||
|
{% assign clickable_line = before_url | append: " in [#" | append: pr_num | append: "](" | append: url | append: ")" %}
|
||||||
|
{% assign processed_lines = processed_lines | append: clickable_line | append: "
|
||||||
|
" %}
|
||||||
|
{% else %}
|
||||||
|
{% assign processed_lines = processed_lines | append: line | append: "
|
||||||
|
" %}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{% assign processed_lines = processed_lines | append: line | append: "
|
||||||
|
" %}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{% assign processed_lines = processed_lines | append: line | append: "
|
||||||
|
" %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% comment %} Add proper spacing between sections and bullet points {% endcomment %}
|
||||||
|
{% assign final_content = processed_lines | replace: "
|
||||||
|
### ", "
|
||||||
|
|
||||||
|
### " %}
|
||||||
|
{% assign final_content = final_content | replace: "
|
||||||
|
## ", "
|
||||||
|
|
||||||
|
## " %}
|
||||||
|
{% assign final_content = final_content | replace: "
|
||||||
|
- ", "
|
||||||
|
|
||||||
|
- " %}
|
||||||
|
|
||||||
|
{% comment %} Check if there are any bullet points (actual release notes) {% endcomment %}
|
||||||
|
{% if final_content contains "## " or final_content contains "- " %}
|
||||||
|
{{ final_content | markdownify }}
|
||||||
|
{% else %}
|
||||||
|
*No release notes available.*
|
||||||
|
|
||||||
|
{{ final_content | markdownify }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
---
|
||||||
|
{% endfor %}
|
||||||
+129
-107
@@ -1,43 +1,49 @@
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (3.0.5)
|
CFPropertyList (3.0.8)
|
||||||
rexml
|
abbrev (0.1.2)
|
||||||
addressable (2.8.0)
|
addressable (2.8.8)
|
||||||
public_suffix (>= 2.0.2, < 5.0)
|
public_suffix (>= 2.0.2, < 8.0)
|
||||||
artifactory (3.0.15)
|
artifactory (3.0.17)
|
||||||
atomos (0.1.3)
|
atomos (0.1.3)
|
||||||
aws-eventstream (1.2.0)
|
aws-eventstream (1.4.0)
|
||||||
aws-partitions (1.554.0)
|
aws-partitions (1.1216.0)
|
||||||
aws-sdk-core (3.126.0)
|
aws-sdk-core (3.242.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.3.0)
|
||||||
aws-partitions (~> 1, >= 1.525.0)
|
aws-partitions (~> 1, >= 1.992.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.9)
|
||||||
jmespath (~> 1.0)
|
base64
|
||||||
aws-sdk-kms (1.54.0)
|
bigdecimal
|
||||||
aws-sdk-core (~> 3, >= 3.126.0)
|
jmespath (~> 1, >= 1.6.1)
|
||||||
aws-sigv4 (~> 1.1)
|
logger
|
||||||
aws-sdk-s3 (1.112.0)
|
aws-sdk-kms (1.122.0)
|
||||||
aws-sdk-core (~> 3, >= 3.126.0)
|
aws-sdk-core (~> 3, >= 3.241.4)
|
||||||
|
aws-sigv4 (~> 1.5)
|
||||||
|
aws-sdk-s3 (1.213.0)
|
||||||
|
aws-sdk-core (~> 3, >= 3.241.4)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.4)
|
aws-sigv4 (~> 1.5)
|
||||||
aws-sigv4 (1.4.0)
|
aws-sigv4 (1.12.1)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
babosa (1.0.4)
|
babosa (1.0.4)
|
||||||
|
base64 (0.2.0)
|
||||||
|
benchmark (0.5.0)
|
||||||
|
bigdecimal (4.0.1)
|
||||||
claide (1.1.0)
|
claide (1.1.0)
|
||||||
colored (1.2)
|
colored (1.2)
|
||||||
colored2 (3.1.2)
|
colored2 (3.1.2)
|
||||||
commander (4.6.0)
|
commander (4.6.0)
|
||||||
highline (~> 2.0.0)
|
highline (~> 2.0.0)
|
||||||
|
csv (3.3.5)
|
||||||
declarative (0.0.20)
|
declarative (0.0.20)
|
||||||
digest-crc (0.6.4)
|
digest-crc (0.7.0)
|
||||||
rake (>= 12.0.0, < 14.0.0)
|
rake (>= 12.0.0, < 14.0.0)
|
||||||
domain_name (0.5.20190701)
|
domain_name (0.6.20240107)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
dotenv (2.8.1)
|
||||||
dotenv (2.7.6)
|
|
||||||
emoji_regex (3.2.3)
|
emoji_regex (3.2.3)
|
||||||
excon (0.91.0)
|
excon (0.112.0)
|
||||||
faraday (1.9.3)
|
faraday (1.10.5)
|
||||||
faraday-em_http (~> 1.0)
|
faraday-em_http (~> 1.0)
|
||||||
faraday-em_synchrony (~> 1.0)
|
faraday-em_synchrony (~> 1.0)
|
||||||
faraday-excon (~> 1.1)
|
faraday-excon (~> 1.1)
|
||||||
@@ -49,32 +55,36 @@ GEM
|
|||||||
faraday-rack (~> 1.0)
|
faraday-rack (~> 1.0)
|
||||||
faraday-retry (~> 1.0)
|
faraday-retry (~> 1.0)
|
||||||
ruby2_keywords (>= 0.0.4)
|
ruby2_keywords (>= 0.0.4)
|
||||||
faraday-cookie_jar (0.0.7)
|
faraday-cookie_jar (0.0.8)
|
||||||
faraday (>= 0.8.0)
|
faraday (>= 0.8.0)
|
||||||
http-cookie (~> 1.0.0)
|
http-cookie (>= 1.0.0)
|
||||||
faraday-em_http (1.0.0)
|
faraday-em_http (1.0.0)
|
||||||
faraday-em_synchrony (1.0.0)
|
faraday-em_synchrony (1.0.1)
|
||||||
faraday-excon (1.1.0)
|
faraday-excon (1.1.0)
|
||||||
faraday-httpclient (1.0.1)
|
faraday-httpclient (1.0.1)
|
||||||
faraday-multipart (1.0.3)
|
faraday-multipart (1.2.0)
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (~> 2.0)
|
||||||
faraday-net_http (1.0.1)
|
faraday-net_http (1.0.2)
|
||||||
faraday-net_http_persistent (1.2.0)
|
faraday-net_http_persistent (1.2.0)
|
||||||
faraday-patron (1.0.0)
|
faraday-patron (1.0.0)
|
||||||
faraday-rack (1.0.0)
|
faraday-rack (1.0.0)
|
||||||
faraday-retry (1.0.3)
|
faraday-retry (1.0.3)
|
||||||
faraday_middleware (1.2.0)
|
faraday_middleware (1.2.1)
|
||||||
faraday (~> 1.0)
|
faraday (~> 1.0)
|
||||||
fastimage (2.2.6)
|
fastimage (2.4.0)
|
||||||
fastlane (2.204.3)
|
fastlane (2.232.1)
|
||||||
CFPropertyList (>= 2.3, < 4.0.0)
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
|
abbrev (~> 0.1.2)
|
||||||
addressable (>= 2.8, < 3.0.0)
|
addressable (>= 2.8, < 3.0.0)
|
||||||
artifactory (~> 3.0)
|
artifactory (~> 3.0)
|
||||||
aws-sdk-s3 (~> 1.0)
|
aws-sdk-s3 (~> 1.197)
|
||||||
babosa (>= 1.0.3, < 2.0.0)
|
babosa (>= 1.0.3, < 2.0.0)
|
||||||
bundler (>= 1.12.0, < 3.0.0)
|
base64 (~> 0.2.0)
|
||||||
colored
|
benchmark (>= 0.1.0)
|
||||||
|
bundler (>= 1.17.3, < 5.0.0)
|
||||||
|
colored (~> 1.2)
|
||||||
commander (~> 4.6)
|
commander (~> 4.6)
|
||||||
|
csv (~> 3.3)
|
||||||
dotenv (>= 2.1.1, < 3.0.0)
|
dotenv (>= 2.1.1, < 3.0.0)
|
||||||
emoji_regex (>= 0.1, < 4.0)
|
emoji_regex (>= 0.1, < 4.0)
|
||||||
excon (>= 0.71.0, < 1.0.0)
|
excon (>= 0.71.0, < 1.0.0)
|
||||||
@@ -82,133 +92,145 @@ GEM
|
|||||||
faraday-cookie_jar (~> 0.0.6)
|
faraday-cookie_jar (~> 0.0.6)
|
||||||
faraday_middleware (~> 1.0)
|
faraday_middleware (~> 1.0)
|
||||||
fastimage (>= 2.1.0, < 3.0.0)
|
fastimage (>= 2.1.0, < 3.0.0)
|
||||||
|
fastlane-sirp (>= 1.0.0)
|
||||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||||
google-apis-androidpublisher_v3 (~> 0.3)
|
google-apis-androidpublisher_v3 (~> 0.3)
|
||||||
google-apis-playcustomapp_v1 (~> 0.1)
|
google-apis-playcustomapp_v1 (~> 0.1)
|
||||||
|
google-cloud-env (>= 1.6.0, <= 2.1.1)
|
||||||
google-cloud-storage (~> 1.31)
|
google-cloud-storage (~> 1.31)
|
||||||
highline (~> 2.0)
|
highline (~> 2.0)
|
||||||
|
http-cookie (~> 1.0.5)
|
||||||
json (< 3.0.0)
|
json (< 3.0.0)
|
||||||
jwt (>= 2.1.0, < 3)
|
jwt (>= 2.1.0, < 3)
|
||||||
|
logger (>= 1.6, < 2.0)
|
||||||
mini_magick (>= 4.9.4, < 5.0.0)
|
mini_magick (>= 4.9.4, < 5.0.0)
|
||||||
multipart-post (~> 2.0.0)
|
multipart-post (>= 2.0.0, < 3.0.0)
|
||||||
|
mutex_m (~> 0.3.0)
|
||||||
naturally (~> 2.2)
|
naturally (~> 2.2)
|
||||||
optparse (~> 0.1.1)
|
nkf (~> 0.2.0)
|
||||||
|
optparse (>= 0.1.1, < 1.0.0)
|
||||||
|
ostruct (>= 0.1.0)
|
||||||
plist (>= 3.1.0, < 4.0.0)
|
plist (>= 3.1.0, < 4.0.0)
|
||||||
rubyzip (>= 2.0.0, < 3.0.0)
|
rubyzip (>= 2.0.0, < 3.0.0)
|
||||||
security (= 0.1.3)
|
security (= 0.1.5)
|
||||||
simctl (~> 1.6.3)
|
simctl (~> 1.6.3)
|
||||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||||
terminal-table (>= 1.4.5, < 2.0.0)
|
terminal-table (~> 3)
|
||||||
tty-screen (>= 0.6.3, < 1.0.0)
|
tty-screen (>= 0.6.3, < 1.0.0)
|
||||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||||
word_wrap (~> 1.0.0)
|
word_wrap (~> 1.0.0)
|
||||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||||
xcpretty (~> 0.3.0)
|
xcpretty (~> 0.4.1)
|
||||||
xcpretty-travis-formatter (>= 0.0.3)
|
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
|
||||||
|
fastlane-sirp (1.0.0)
|
||||||
|
sysrandom (~> 1.0)
|
||||||
gh_inspector (1.1.3)
|
gh_inspector (1.1.3)
|
||||||
google-apis-androidpublisher_v3 (0.16.0)
|
google-apis-androidpublisher_v3 (0.96.0)
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
google-apis-core (>= 0.15.0, < 2.a)
|
||||||
google-apis-core (0.4.2)
|
google-apis-core (0.18.0)
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
googleauth (~> 1.9)
|
||||||
httpclient (>= 2.8.1, < 3.a)
|
httpclient (>= 2.8.3, < 3.a)
|
||||||
mini_mime (~> 1.0)
|
mini_mime (~> 1.0)
|
||||||
|
mutex_m
|
||||||
representable (~> 3.0)
|
representable (~> 3.0)
|
||||||
retriable (>= 2.0, < 4.a)
|
retriable (>= 2.0, < 4.a)
|
||||||
rexml
|
google-apis-iamcredentials_v1 (0.26.0)
|
||||||
webrick
|
google-apis-core (>= 0.15.0, < 2.a)
|
||||||
google-apis-iamcredentials_v1 (0.10.0)
|
google-apis-playcustomapp_v1 (0.17.0)
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
google-apis-core (>= 0.15.0, < 2.a)
|
||||||
google-apis-playcustomapp_v1 (0.7.0)
|
google-apis-storage_v1 (0.60.0)
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
google-apis-core (>= 0.15.0, < 2.a)
|
||||||
google-apis-storage_v1 (0.11.0)
|
google-cloud-core (1.8.0)
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
google-cloud-env (>= 1.0, < 3.a)
|
||||||
google-cloud-core (1.6.0)
|
|
||||||
google-cloud-env (~> 1.0)
|
|
||||||
google-cloud-errors (~> 1.0)
|
google-cloud-errors (~> 1.0)
|
||||||
google-cloud-env (1.5.0)
|
google-cloud-env (2.1.1)
|
||||||
faraday (>= 0.17.3, < 2.0)
|
faraday (>= 1.0, < 3.a)
|
||||||
google-cloud-errors (1.2.0)
|
google-cloud-errors (1.5.0)
|
||||||
google-cloud-storage (1.36.1)
|
google-cloud-storage (1.58.0)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
digest-crc (~> 0.4)
|
digest-crc (~> 0.4)
|
||||||
google-apis-iamcredentials_v1 (~> 0.1)
|
google-apis-core (>= 0.18, < 2)
|
||||||
google-apis-storage_v1 (~> 0.1)
|
google-apis-iamcredentials_v1 (~> 0.18)
|
||||||
|
google-apis-storage_v1 (>= 0.42)
|
||||||
google-cloud-core (~> 1.6)
|
google-cloud-core (~> 1.6)
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
googleauth (~> 1.9)
|
||||||
mini_mime (~> 1.0)
|
mini_mime (~> 1.0)
|
||||||
googleauth (1.1.0)
|
googleauth (1.11.2)
|
||||||
faraday (>= 0.17.3, < 2.0)
|
faraday (>= 1.0, < 3.a)
|
||||||
|
google-cloud-env (~> 2.1)
|
||||||
jwt (>= 1.4, < 3.0)
|
jwt (>= 1.4, < 3.0)
|
||||||
memoist (~> 0.16)
|
|
||||||
multi_json (~> 1.11)
|
multi_json (~> 1.11)
|
||||||
os (>= 0.9, < 2.0)
|
os (>= 0.9, < 2.0)
|
||||||
signet (>= 0.16, < 2.a)
|
signet (>= 0.16, < 2.a)
|
||||||
highline (2.0.3)
|
highline (2.0.3)
|
||||||
http-cookie (1.0.4)
|
http-cookie (1.0.8)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
httpclient (2.8.3)
|
httpclient (2.9.0)
|
||||||
jmespath (1.5.0)
|
mutex_m
|
||||||
json (2.6.1)
|
jmespath (1.6.2)
|
||||||
jwt (2.3.0)
|
json (2.18.1)
|
||||||
memoist (0.16.2)
|
jwt (2.10.2)
|
||||||
mini_magick (4.11.0)
|
base64
|
||||||
mini_mime (1.1.2)
|
logger (1.7.0)
|
||||||
multi_json (1.15.0)
|
mini_magick (4.13.2)
|
||||||
multipart-post (2.0.0)
|
mini_mime (1.1.5)
|
||||||
nanaimo (0.3.0)
|
multi_json (1.19.1)
|
||||||
naturally (2.2.1)
|
multipart-post (2.4.1)
|
||||||
optparse (0.1.1)
|
mutex_m (0.3.0)
|
||||||
|
nanaimo (0.4.0)
|
||||||
|
naturally (2.3.0)
|
||||||
|
nkf (0.2.0)
|
||||||
|
optparse (0.8.1)
|
||||||
os (1.1.4)
|
os (1.1.4)
|
||||||
plist (3.6.0)
|
ostruct (0.6.3)
|
||||||
public_suffix (4.0.6)
|
plist (3.7.2)
|
||||||
rake (13.0.6)
|
public_suffix (7.0.2)
|
||||||
representable (3.1.1)
|
rake (13.3.1)
|
||||||
|
representable (3.2.0)
|
||||||
declarative (< 0.1.0)
|
declarative (< 0.1.0)
|
||||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||||
uber (< 0.2.0)
|
uber (< 0.2.0)
|
||||||
retriable (3.1.2)
|
retriable (3.2.0)
|
||||||
rexml (3.2.5)
|
rexml (3.4.4)
|
||||||
rouge (2.0.7)
|
rouge (3.28.0)
|
||||||
ruby2_keywords (0.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
rubyzip (2.3.2)
|
rubyzip (2.4.1)
|
||||||
security (0.1.3)
|
security (0.1.5)
|
||||||
signet (0.16.0)
|
signet (0.21.0)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
faraday (>= 0.17.3, < 2.0)
|
faraday (>= 0.17.5, < 3.a)
|
||||||
jwt (>= 1.5, < 3.0)
|
jwt (>= 1.5, < 4.0)
|
||||||
multi_json (~> 1.10)
|
multi_json (~> 1.10)
|
||||||
simctl (1.6.8)
|
simctl (1.6.10)
|
||||||
CFPropertyList
|
CFPropertyList
|
||||||
naturally
|
naturally
|
||||||
|
sysrandom (1.0.5)
|
||||||
terminal-notifier (2.0.0)
|
terminal-notifier (2.0.0)
|
||||||
terminal-table (1.8.0)
|
terminal-table (3.0.2)
|
||||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
unicode-display_width (>= 1.1.1, < 3)
|
||||||
trailblazer-option (0.1.2)
|
trailblazer-option (0.1.2)
|
||||||
tty-cursor (0.7.1)
|
tty-cursor (0.7.1)
|
||||||
tty-screen (0.8.1)
|
tty-screen (0.8.2)
|
||||||
tty-spinner (0.9.3)
|
tty-spinner (0.9.3)
|
||||||
tty-cursor (~> 0.7)
|
tty-cursor (~> 0.7)
|
||||||
uber (0.1.0)
|
uber (0.1.0)
|
||||||
unf (0.1.4)
|
unicode-display_width (2.6.0)
|
||||||
unf_ext
|
|
||||||
unf_ext (0.0.8)
|
|
||||||
unicode-display_width (1.8.0)
|
|
||||||
webrick (1.7.0)
|
|
||||||
word_wrap (1.0.0)
|
word_wrap (1.0.0)
|
||||||
xcodeproj (1.21.0)
|
xcodeproj (1.27.0)
|
||||||
CFPropertyList (>= 2.3.3, < 4.0)
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
atomos (~> 0.1.3)
|
atomos (~> 0.1.3)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
colored2 (~> 3.1)
|
colored2 (~> 3.1)
|
||||||
nanaimo (~> 0.3.0)
|
nanaimo (~> 0.4.0)
|
||||||
rexml (~> 3.2.4)
|
rexml (>= 3.3.6, < 4.0)
|
||||||
xcpretty (0.3.0)
|
xcpretty (0.4.1)
|
||||||
rouge (~> 2.0.7)
|
rouge (~> 3.28.0)
|
||||||
xcpretty-travis-formatter (1.0.1)
|
xcpretty-travis-formatter (1.0.1)
|
||||||
xcpretty (~> 0.2, >= 0.0.7)
|
xcpretty (~> 0.2, >= 0.0.7)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
|
universal-darwin-24
|
||||||
x86_64-linux
|
x86_64-linux
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
|||||||
+55
-11
@@ -1,24 +1,68 @@
|
|||||||
# Privacy policy for the app `CAPod`
|
---
|
||||||
|
layout: plain
|
||||||
|
permalink: /privacy
|
||||||
|
title: "Privacy Policy"
|
||||||
|
---
|
||||||
|
|
||||||
* I do not collect personal information
|
# Privacy policy
|
||||||
* I do not sell, monetize or otherwise misappropriate any collected data.
|
This is the privacy policy for the Android app "CAPod - Companion for AirPods" by Matthias Urhahn (darken).
|
||||||
|
|
||||||
Anonymous device information may be collected in the event of a crash (see [Automatic crash reports](#automatic-crash-reports)).
|
## Preamble
|
||||||
|
CAPod respects your privacy.
|
||||||
|
|
||||||
|
I do not collect, share or sell personal information.
|
||||||
|
|
||||||
|
Send a [quick mail](mailto:support@darken.eu) if you have questions.
|
||||||
|
|
||||||
My underlying privacy principle is the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule).
|
My underlying privacy principle is the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule).
|
||||||
|
|
||||||
Send a [quick mail](mailto:support@darken.eu) if you have further questions.
|
## Location data
|
||||||
|
|
||||||
## Automatic crash reports
|
CAPod does not collect, share or sell location data.
|
||||||
|
|
||||||
The app uses "Bugsnag" for automatic crash reports:
|
Location permissions are required to receive Bluetooth Low Energy (BLE) data and ebale its core functionality.
|
||||||
|
The permission "access fine location" (`ACCESS_FINE_LOCATION`) and "access coarse location" (`ACCESS_COARSE_LOCATION`) are required to receive Bluetooth Low Energy data on Android 11 and lower.
|
||||||
|
On Android 12+ the newer and more fine grained `BLUETOOTH_SCAN` permission is used instead.
|
||||||
|
|
||||||
|
Bluetooth Low Energy is a technology that devices like AirPods use to communicate their status to nearby devices.
|
||||||
|
CAPod requests location permissions because these permissions are required to work with Bluetooth Low Energy data.
|
||||||
|
This is a privacy measure on Android's side because you could determine someones location by scanning for Bluetooth
|
||||||
|
devices:
|
||||||
|
If you know the physical location of a Bluetooth device (e.g. AirTags) you could use Bluetooth data to calculate your
|
||||||
|
position.
|
||||||
|
|
||||||
|
### Location access in the background
|
||||||
|
|
||||||
|
CAPod uses the "location access in the background" permission (`ACCESS_BACKGROUND_LOCATION`) on Android 11 and older to
|
||||||
|
receive Bluetooth Low Energy data while the app is in the background. This permission enables the "Show popup" and "
|
||||||
|
Autoconnect" features and allows CAPod to react to nearby devices whil the app is closed.
|
||||||
|
|
||||||
|
## Automatic error reports
|
||||||
|
|
||||||
|
*This was removed in v2.11.0+*
|
||||||
|
|
||||||
|
If an error occurs, an automated report may be sent to help me fix the issue.
|
||||||
|
|
||||||
|
This is optional and you can opt out of this in the settings.
|
||||||
|
|
||||||
|
Error reports are collected using "Bugsnag":
|
||||||
https://www.bugsnag.com/
|
https://www.bugsnag.com/
|
||||||
|
|
||||||
Bugsnags privacy policy can be found here:
|
Bugsnag's privacy policy can be found here:
|
||||||
|
|
||||||
https://docs.bugsnag.com/legal/privacy-policy/
|
https://docs.bugsnag.com/legal/privacy-policy/
|
||||||
|
|
||||||
Crash reports may contain device and app related information.
|
Error reports contain device and app information related to the error that occured.
|
||||||
|
Additional information about the error context may also be included, e.g. what this app did shortly before the error.
|
||||||
|
|
||||||
You can disable automatic crash reports in the app's settings.
|
Additional details about the type of data that is collected by the error tracking SDK can be found here:
|
||||||
|
https://docs.bugsnag.com/platforms/android/playstore-privacy/
|
||||||
|
|
||||||
|
Error reports are pseudonymous. Unless you tell me your install-ID, I don't know that an error report came from you.
|
||||||
|
|
||||||
|
Error reports are automatically deleted after 90 days.
|
||||||
|
|
||||||
|
## Debug logs
|
||||||
|
|
||||||
|
The app has a debug log feature that can be used to assist troubleshooting efforts. This feature creates a log file that contains verbose output of what the app is doing.
|
||||||
|
|
||||||
|
It is manually triggered by the user through an option in the app settings. The recorded log file can be shared through compatible apps (e.g. your email app) using the system's share dialog. As this log file may contain sensitive information (e.g. details about files or installed applications) it should only be shared with trusted parties.
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
<img src="https://github.com/d4rken/capod/raw/main/.assets/banner.png" width="400">
|
<img src="https://github.com/d4rken-org/capod/raw/main/.assets/banner.png" width="400">
|
||||||
|
|
||||||
# Companion App for AirPods (CAPod)
|
# Companion App for AirPods (CAPod)
|
||||||
|
|
||||||

|
[](https://github.com/d4rken-org/capod/releases/latest)
|
||||||
[](https://github.com/d4rken/capod/releases/latest)
|
[](https://shields.rbtlog.dev/eu.darken.capod)
|
||||||
|
[](https://github.com/d4rken/capod/actions/workflows/code-checks.yml)
|
||||||
|
[](https://crowdin.com/project/capod)
|
||||||
|
[](https://github.com/d4rken-org/capod/edit/main/README.md#download)
|
||||||
|
[](https://github.com/d4rken-org/capod/edit/main/README.md#download)
|
||||||
|
|
||||||
A companion app that adds support for AirPod specific features to Android:
|
A companion app that adds support for AirPod specific features to Android:
|
||||||
|
|
||||||
@@ -14,41 +18,62 @@ A companion app that adds support for AirPod specific features to Android:
|
|||||||
* Ear detection with automatic play/pause.
|
* Ear detection with automatic play/pause.
|
||||||
* Automatically connect phone & AirPods.
|
* Automatically connect phone & AirPods.
|
||||||
* Show popup when case is opened.
|
* Show popup when case is opened.
|
||||||
|
* Widgets
|
||||||
|
|
||||||
CAPod is ad-free. Some additional features require an in-app purchase.
|
CAPod is ad-free. Some additional features require an in-app purchase.
|
||||||
|
|
||||||
Currently supported models:
|
Currently supported models:
|
||||||
|
|
||||||
* AirPods Gen1
|
* AirPods 1. Generation
|
||||||
* AirPods Gen2
|
* AirPods 2. Generation
|
||||||
* AirPods Pro
|
* AirPods 3. Generation
|
||||||
|
* AirPods 4. Generation
|
||||||
|
* AirPods 4. Generation with ANC
|
||||||
|
* AirPods Pro 1. Generation
|
||||||
|
* AirPods Pro 2. Generation
|
||||||
|
* AirPods Pro 2. Generation (USB-C)
|
||||||
|
* AirPods Pro 3. Generation
|
||||||
* AirPods Max
|
* AirPods Max
|
||||||
* Power Beats Pro
|
* Power Beats Pro
|
||||||
* Power Beats 3
|
* Power Beats 3
|
||||||
|
* Power Beats 4
|
||||||
* Beats Solo 3
|
* Beats Solo 3
|
||||||
* Beats Studio 3
|
* Beats Studio 3
|
||||||
* Beats X
|
* Beats X
|
||||||
* Beats Flex
|
* Beats Flex
|
||||||
|
* Beats Fit Pro
|
||||||
|
* Fake AirPods 1. Generation 🎭
|
||||||
|
* Fake AirPods 2. Generation 🎭
|
||||||
|
* Fake AirPods 3. Generation 🎭
|
||||||
|
* Fake AirPods Pro1 🎭
|
||||||
|
* Fake AirPods Pro2 🎭
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
* [Google Play](https://play.google.com/store/apps/details?id=eu.darken.capod)
|
| Source | Status |
|
||||||
* [GitHub](https://github.com/d4rken/capod/releases/latest)
|
|------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| [Google Play](https://play.google.com/store/apps/details?id=eu.darken.capod) | [](https://play.google.com/store/apps/details?id=eu.darken.capod) |
|
||||||
|
| [Google Play Beta](https://play.google.com/apps/testing/eu.darken.capod) | [](https://play.google.com/apps/testing/eu.darken.capod) | |
|
||||||
|
| [Github (Release)](https://github.com/d4rken-org/capod/releases) | )  |
|
||||||
|
| [Github (Pre-Release)](https://github.com/d4rken-org/capod/releases) | )  |
|
||||||
|
| [F-Droid](https://f-droid.org/en/packages/eu.darken.capod/) | [](https://f-droid.org/en/packages/eu.darken.capod/) |
|
||||||
|
| [F-Droid (IzzyOnDroid)](https://apt.izzysoft.de/packages/eu.darken.capod/) | [](https://apt.izzysoft.de/packages/eu.darken.capod/) |
|
||||||
|
|
||||||
## Support the project
|
## Support the project
|
||||||
|
|
||||||
* Buy the CAPod Pro In-App purchase on [Google Play](https://play.google.com/store/apps/details?id=eu.darken.capod)
|
* Buy the CAPod Pro In-App purchase on [Google Play](https://play.google.com/store/apps/details?id=eu.darken.capod)
|
||||||
|
* [Sponsor development](https://github.com/sponsors/d4rken) on GitHub
|
||||||
* Help translate CAPod [on Crowdin](https://crowdin.com/project/capod)
|
* Help translate CAPod [on Crowdin](https://crowdin.com/project/capod)
|
||||||
* [Buy me a coffee](https://www.buymeacoffee.com/tydarken)
|
|
||||||
|
|
||||||
## Get help
|
## Get help
|
||||||
|
|
||||||
* [Github Issues](https://github.com/d4rken/capod/issues)
|
* [Github Issues](https://github.com/d4rken-org/capod/issues)
|
||||||
* [Discord](https://discord.gg/vHubYPp)
|
* [Discord](https://discord.gg/vHubYPp)
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
<img src="https://github.com/d4rken/capod/raw/main/.assets/screenshots/1.png" width="200"><img src="https://github.com/d4rken/capod/raw/main/.assets/screenshots/2.png" width="200"><img src="https://github.com/d4rken/capod/raw/main/.assets/screenshots/3.png" width="200"><img src="https://github.com/d4rken/capod/raw/main/.assets/screenshots/4.png" width="200">
|
<img src="https://github.com/d4rken-org/capod/raw/main/.assets/screenshots/1.png" width="200"><img src="https://github.com/d4rken-org/capod/raw/main/.assets/screenshots/2.png" width="200"><img src="https://github.com/d4rken-org/capod/raw/main/.assets/screenshots/3.png" width="200"><img src="https://github.com/d4rken-org/capod/raw/main/.assets/screenshots/4.png" width="200">
|
||||||
|
<img src="https://raw.githubusercontent.com/d4rken-org/capod/main/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png" width="200">
|
||||||
|
|
||||||
## Thanks to
|
## Thanks to
|
||||||
|
|
||||||
@@ -58,6 +83,10 @@ Currently supported models:
|
|||||||
Travis & Rye, Erik & Sipes, Brandon & Teplov, Sam. (2019). Handoff All Your Privacy – A Review of Apple’s Bluetooth
|
Travis & Rye, Erik & Sipes, Brandon & Teplov, Sam. (2019). Handoff All Your Privacy – A Review of Apple’s Bluetooth
|
||||||
Low Energy Continuity Protocol. Proceedings on Privacy Enhancing Technologies. 2019. 34-53. 10.2478/popets-2019-0057.
|
Low Energy Continuity Protocol. Proceedings on Privacy Enhancing Technologies. 2019. 34-53. 10.2478/popets-2019-0057.
|
||||||
* Celosia, Guillaume. (2020). Privacy Challenges in Wireless Communications of the Internet of Things.
|
* Celosia, Guillaume. (2020). Privacy Challenges in Wireless Communications of the Internet of Things.
|
||||||
|
* The [MagicPods](https://github.com/steam3d/MagicPods-Windows) project. If you are looking for "CAPod for Windows",
|
||||||
|
check it out.
|
||||||
|
* [@kavishdevar](https://github.com/kavishdevar/librepods) and
|
||||||
|
his [LibrePods project](https://github.com/kavishdevar/librepods) for sharing a lot of cool stuff.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
+28
@@ -0,0 +1,28 @@
|
|||||||
|
theme: minima
|
||||||
|
plugins:
|
||||||
|
- jekyll-relative-links
|
||||||
|
- jekyll-github-metadata
|
||||||
|
- jemoji
|
||||||
|
|
||||||
|
relative_links:
|
||||||
|
enabled: true
|
||||||
|
collections: true
|
||||||
|
|
||||||
|
title: "CAPod"
|
||||||
|
description: "A companion app for AirPods on Android."
|
||||||
|
author: "by Matthias Urhahn"
|
||||||
|
|
||||||
|
include:
|
||||||
|
- PRIVACY_POLICY.md
|
||||||
|
- README.md
|
||||||
|
- CHANGELOG.md
|
||||||
|
exclude:
|
||||||
|
- buildSrc
|
||||||
|
- gradle/wrapper
|
||||||
|
- fastlane
|
||||||
|
- Gemfile
|
||||||
|
- Gemfile.lock
|
||||||
|
- crowdin*
|
||||||
|
- app
|
||||||
|
- app-common
|
||||||
|
- CONTRIBUTING.md
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{{ page.lang | default: site.lang | default: 'en' }}">
|
||||||
|
|
||||||
|
{%- include head.html -%}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<main class="page-content" aria-label="Content">
|
||||||
|
<div class="wrapper">
|
||||||
|
{{ content }}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{%- include footer.html -%}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -1,266 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'com.android.application'
|
|
||||||
id 'kotlin-android'
|
|
||||||
id 'kotlin-kapt'
|
|
||||||
id 'kotlin-parcelize'
|
|
||||||
id 'androidx.navigation.safeargs.kotlin'
|
|
||||||
id 'com.bugsnag.android.gradle'
|
|
||||||
id 'dagger.hilt.android.plugin'
|
|
||||||
}
|
|
||||||
|
|
||||||
def gitSha = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim()
|
|
||||||
def buildTime = new Date().format("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("GMT+1"))
|
|
||||||
|
|
||||||
android {
|
|
||||||
def packageName = "eu.darken.capod"
|
|
||||||
|
|
||||||
compileSdkVersion buildConfig.compileSdk
|
|
||||||
|
|
||||||
Properties bugsnagProps = new Properties()
|
|
||||||
def bugsnagPropsFile = new File(System.properties['user.home'], ".appconfig/${packageName}/bugsnag.properties")
|
|
||||||
if (bugsnagPropsFile.canRead()) bugsnagProps.load(new FileInputStream(bugsnagPropsFile))
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
applicationId "${packageName}"
|
|
||||||
|
|
||||||
minSdkVersion buildConfig.minSdk
|
|
||||||
targetSdkVersion buildConfig.targetSdk
|
|
||||||
|
|
||||||
versionCode buildConfig.version.code
|
|
||||||
versionName buildConfig.version.name
|
|
||||||
|
|
||||||
testInstrumentationRunner "eu.darken.capod.HiltTestRunner"
|
|
||||||
|
|
||||||
buildConfigField "String", "GITSHA", "\"${gitSha}\""
|
|
||||||
buildConfigField "String", "BUILDTIME", "\"${buildTime}\""
|
|
||||||
|
|
||||||
manifestPlaceholders = [bugsnagApiKey: "fake"]
|
|
||||||
}
|
|
||||||
|
|
||||||
signingConfigs {
|
|
||||||
release {}
|
|
||||||
}
|
|
||||||
def signingPropFile = new File(System.properties['user.home'], ".appconfig/${packageName}/signing.properties")
|
|
||||||
if (signingPropFile.canRead()) {
|
|
||||||
Properties signingProps = new Properties()
|
|
||||||
signingProps.load(new FileInputStream(signingPropFile))
|
|
||||||
signingConfigs {
|
|
||||||
release {
|
|
||||||
storeFile new File(signingProps['release.storePath'])
|
|
||||||
keyAlias signingProps['release.keyAlias']
|
|
||||||
storePassword signingProps['release.storePassword']
|
|
||||||
keyPassword signingProps['release.keyPassword']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
def proguardRulesRelease = fileTree(dir: "../proguard", include: ["*.pro"]).asList().toArray()
|
|
||||||
debug {
|
|
||||||
minifyEnabled false
|
|
||||||
shrinkResources false
|
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
|
|
||||||
proguardFiles proguardRulesRelease
|
|
||||||
proguardFiles 'proguard-rules-debug.pro'
|
|
||||||
manifestPlaceholders = [bugsnagApiKey: bugsnagProps.getProperty("bugsnag.apikey", "")]
|
|
||||||
}
|
|
||||||
release {
|
|
||||||
signingConfig signingConfigs.release
|
|
||||||
lintOptions {
|
|
||||||
abortOnError true
|
|
||||||
fatal 'StopShip'
|
|
||||||
}
|
|
||||||
minifyEnabled true
|
|
||||||
shrinkResources true
|
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
|
|
||||||
proguardFiles proguardRulesRelease
|
|
||||||
manifestPlaceholders = [bugsnagApiKey: bugsnagProps.getProperty("bugsnag.apikey", "")]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flavorDimensions "version"
|
|
||||||
productFlavors {
|
|
||||||
gplay {
|
|
||||||
|
|
||||||
}
|
|
||||||
foss {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
applicationVariants.all { variant ->
|
|
||||||
def flavor = variant.productFlavors[0].name.toUpperCase()
|
|
||||||
def buildType = variant.buildType.name.toUpperCase()
|
|
||||||
def versionCode = defaultConfig.versionCode
|
|
||||||
def versionName = defaultConfig.versionName
|
|
||||||
|
|
||||||
if (variant.buildType.name == "debug") {
|
|
||||||
variant.mergedFlavor.resourceConfigurations.clear()
|
|
||||||
variant.mergedFlavor.resourceConfigurations.add("en")
|
|
||||||
variant.mergedFlavor.resourceConfigurations.add("de")
|
|
||||||
} else if (variant.buildType.name != "debug") {
|
|
||||||
variant.outputs.each { output ->
|
|
||||||
output.outputFileName = "${packageName}-v${versionName}(${versionCode})-${gitSha}-${flavor}-${buildType}.apk"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = '1.8'
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
viewBinding true
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
|
||||||
|
|
||||||
freeCompilerArgs += [
|
|
||||||
"-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
|
||||||
"-Xuse-experimental=kotlinx.coroutines.FlowPreview",
|
|
||||||
"-Xuse-experimental=kotlin.time.ExperimentalTime",
|
|
||||||
"-Xuse-experimental=kotlin.ExperimentalUnsignedTypes",
|
|
||||||
"-Xuse-experimental=kotlin.contracts.ExperimentalContracts",
|
|
||||||
"-Xopt-in=kotlin.RequiresOptIn"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testOptions {
|
|
||||||
unitTests.all {
|
|
||||||
useJUnitPlatform()
|
|
||||||
}
|
|
||||||
unitTests {
|
|
||||||
includeAndroidResources = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
test {
|
|
||||||
java.srcDirs += "$projectDir/src/testShared/java"
|
|
||||||
}
|
|
||||||
androidTest {
|
|
||||||
java.srcDirs += "$projectDir/src/testShared/java"
|
|
||||||
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// Kotlin
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin.core}"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.kotlin.coroutines}"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.kotlin.coroutines}"
|
|
||||||
|
|
||||||
testImplementation "org.jetbrains.kotlin:kotlin-reflect:${versions.kotlin.core}"
|
|
||||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:${versions.kotlin.coroutines}"
|
|
||||||
androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${versions.kotlin.coroutines}") {
|
|
||||||
// conflicts with mockito due to direct inclusion of byte buddy
|
|
||||||
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debugging
|
|
||||||
implementation('com.bugsnag:bugsnag-android:5.9.2')
|
|
||||||
implementation 'com.getkeepsafe.relinker:relinker:1.4.3'
|
|
||||||
|
|
||||||
implementation("com.squareup.moshi:moshi:1.13.0")
|
|
||||||
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.13.0")
|
|
||||||
|
|
||||||
// DI
|
|
||||||
implementation "com.google.dagger:dagger:${versions.dagger.core}"
|
|
||||||
implementation "com.google.dagger:dagger-android:${versions.dagger.core}"
|
|
||||||
|
|
||||||
kapt "com.google.dagger:dagger-compiler:${versions.dagger.core}"
|
|
||||||
kapt "com.google.dagger:dagger-android-processor:${versions.dagger.core}"
|
|
||||||
|
|
||||||
implementation "com.google.dagger:hilt-android:${versions.dagger.core}"
|
|
||||||
kapt "com.google.dagger:hilt-android-compiler:${versions.dagger.core}"
|
|
||||||
|
|
||||||
testImplementation "com.google.dagger:hilt-android-testing:${versions.dagger.core}"
|
|
||||||
kaptTest "com.google.dagger:hilt-android-compiler:${versions.dagger.core}"
|
|
||||||
|
|
||||||
androidTestImplementation "com.google.dagger:hilt-android-testing:${versions.dagger.core}"
|
|
||||||
kaptAndroidTest "com.google.dagger:hilt-android-compiler:${versions.dagger.core}"
|
|
||||||
|
|
||||||
kapt "androidx.hilt:hilt-compiler:1.0.0"
|
|
||||||
implementation 'androidx.hilt:hilt-common:1.0.0'
|
|
||||||
kaptTest "androidx.hilt:hilt-compiler:1.0.0"
|
|
||||||
testImplementation 'androidx.hilt:hilt-common:1.0.0'
|
|
||||||
|
|
||||||
// Support libs
|
|
||||||
implementation 'androidx.core:core-ktx:1.7.0'
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.4.0'
|
|
||||||
implementation 'androidx.annotation:annotation:1.3.0'
|
|
||||||
|
|
||||||
implementation 'androidx.activity:activity-ktx:1.4.0'
|
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.4.0'
|
|
||||||
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.4.0'
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-common-java8:2.4.0'
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-process:2.4.0'
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0'
|
|
||||||
|
|
||||||
implementation "androidx.navigation:navigation-fragment-ktx:2.3.5"
|
|
||||||
implementation "androidx.navigation:navigation-ui-ktx:2.3.5"
|
|
||||||
|
|
||||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
|
||||||
|
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'
|
|
||||||
|
|
||||||
|
|
||||||
def work_version = "2.7.1"
|
|
||||||
implementation "androidx.work:work-runtime:${work_version}"
|
|
||||||
testImplementation "androidx.work:work-testing:${work_version}"
|
|
||||||
implementation "androidx.work:work-runtime-ktx:${work_version}"
|
|
||||||
implementation 'androidx.hilt:hilt-work:1.0.0'
|
|
||||||
|
|
||||||
// IAP
|
|
||||||
gplayImplementation 'com.android.billingclient:billing:4.0.0'
|
|
||||||
|
|
||||||
// UI
|
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
|
|
||||||
implementation 'com.google.android.material:material:1.6.0-alpha01'
|
|
||||||
|
|
||||||
// Testing
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
|
||||||
testImplementation "org.junit.vintage:junit-vintage-engine:5.7.1"
|
|
||||||
testImplementation "androidx.test:core-ktx:1.4.0"
|
|
||||||
|
|
||||||
testImplementation "io.mockk:mockk:1.12.1"
|
|
||||||
androidTestImplementation "io.mockk:mockk-android:1.11.0"
|
|
||||||
|
|
||||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.7.1"
|
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:5.7.1"
|
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-params:5.7.1"
|
|
||||||
|
|
||||||
androidTestImplementation "androidx.navigation:navigation-testing:2.3.5"
|
|
||||||
|
|
||||||
testImplementation "io.kotest:kotest-runner-junit5:4.6.2"
|
|
||||||
testImplementation "io.kotest:kotest-assertions-core-jvm:4.6.2"
|
|
||||||
testImplementation "io.kotest:kotest-property-jvm:4.6.2"
|
|
||||||
androidTestImplementation "io.kotest:kotest-assertions-core-jvm:4.6.2"
|
|
||||||
androidTestImplementation "io.kotest:kotest-property-jvm:4.6.2"
|
|
||||||
|
|
||||||
testImplementation 'android.arch.core:core-testing:1.1.1'
|
|
||||||
androidTestImplementation 'android.arch.core:core-testing:1.1.1'
|
|
||||||
debugImplementation 'androidx.test:core-ktx:1.4.0'
|
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
|
||||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.4.0'
|
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.4.0'
|
|
||||||
androidTestImplementation 'androidx.test.espresso.idling:idling-concurrent:3.4.0'
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
plugins {
|
||||||
|
id("projectConfig")
|
||||||
|
id("com.android.application")
|
||||||
|
id("kotlin-android")
|
||||||
|
id("com.google.devtools.ksp")
|
||||||
|
id("kotlin-parcelize")
|
||||||
|
id("org.jetbrains.kotlin.plugin.compose")
|
||||||
|
id("org.jetbrains.kotlin.plugin.serialization")
|
||||||
|
id("com.android.compose.screenshot") version "0.0.1-alpha13"
|
||||||
|
}
|
||||||
|
apply(plugin = "dagger.hilt.android.plugin")
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdk = projectConfig.compileSdk
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
namespace = projectConfig.packageName
|
||||||
|
|
||||||
|
minSdk = projectConfig.minSdk
|
||||||
|
targetSdk = projectConfig.targetSdk
|
||||||
|
|
||||||
|
versionCode = projectConfig.version.code.toInt()
|
||||||
|
versionName = projectConfig.version.name
|
||||||
|
|
||||||
|
testInstrumentationRunner = "eu.darken.capod.HiltTestRunner"
|
||||||
|
|
||||||
|
buildConfigField("String", "PACKAGENAME", "\"${projectConfig.packageName}\"")
|
||||||
|
buildConfigField("String", "VERSION_CODE", "\"${projectConfig.version.code}\"")
|
||||||
|
buildConfigField("String", "VERSION_NAME", "\"${projectConfig.version.name}\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable automatic per-app language preferences generation
|
||||||
|
androidResources {
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
|
generateLocaleConfig = true
|
||||||
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
val basePath = File(System.getProperty("user.home"), ".config/projects/${projectConfig.packageName}")
|
||||||
|
create("releaseFoss") {
|
||||||
|
setupCredentials(File(basePath, "signing-foss.properties"))
|
||||||
|
}
|
||||||
|
create("releaseGplay") {
|
||||||
|
setupCredentials(File(basePath, "signing-gplay-upload.properties"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flavorDimensions.add("version")
|
||||||
|
productFlavors {
|
||||||
|
create("foss") {
|
||||||
|
dimension = "version"
|
||||||
|
signingConfig = signingConfigs["releaseFoss"]
|
||||||
|
// The info block is encrypted and can only be read by google
|
||||||
|
dependenciesInfo {
|
||||||
|
includeInApk = false
|
||||||
|
includeInBundle = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
create("gplay") {
|
||||||
|
dimension = "version"
|
||||||
|
signingConfig = signingConfigs["releaseGplay"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
val customProguardRules = fileTree(File(projectDir, "proguard")) {
|
||||||
|
include("*.pro")
|
||||||
|
}
|
||||||
|
debug {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
isShrinkResources = false
|
||||||
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
|
||||||
|
proguardFiles(*customProguardRules.toList().toTypedArray())
|
||||||
|
proguardFiles("proguard-rules-debug.pro")
|
||||||
|
}
|
||||||
|
create("beta") {
|
||||||
|
lint {
|
||||||
|
abortOnError = true
|
||||||
|
fatal.add("StopShip")
|
||||||
|
}
|
||||||
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = true
|
||||||
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
|
||||||
|
proguardFiles(*customProguardRules.toList().toTypedArray())
|
||||||
|
}
|
||||||
|
release {
|
||||||
|
lint {
|
||||||
|
abortOnError = true
|
||||||
|
fatal.add("StopShip")
|
||||||
|
}
|
||||||
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = true
|
||||||
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
|
||||||
|
proguardFiles(*customProguardRules.toList().toTypedArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
experimentalProperties["android.experimental.enableScreenshotTest"] = true
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
viewBinding = true
|
||||||
|
buildConfig = true
|
||||||
|
compose = true
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "17"
|
||||||
|
freeCompilerArgs = freeCompilerArgs + listOf(
|
||||||
|
"-opt-in=kotlin.ExperimentalStdlibApi",
|
||||||
|
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||||
|
"-opt-in=kotlinx.coroutines.FlowPreview",
|
||||||
|
"-opt-in=kotlin.time.ExperimentalTime",
|
||||||
|
"-opt-in=kotlin.RequiresOptIn",
|
||||||
|
"-opt-in=kotlin.ExperimentalUnsignedTypes",
|
||||||
|
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
|
||||||
|
"-Xannotation-default-target=param-property"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
unitTests {
|
||||||
|
isIncludeAndroidResources = true
|
||||||
|
}
|
||||||
|
//noinspection WrongGradleMethod
|
||||||
|
tasks.withType<Test> {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
androidComponents {
|
||||||
|
onVariants { variant ->
|
||||||
|
val buildType = variant.buildType ?: return@onVariants
|
||||||
|
if (buildType != "release" && buildType != "beta") return@onVariants
|
||||||
|
|
||||||
|
val formattedVariantName = variant.name
|
||||||
|
.replace(Regex("([a-z])([A-Z])"), "$1-$2")
|
||||||
|
.uppercase()
|
||||||
|
|
||||||
|
val apkFolder = variant.artifacts.get(com.android.build.api.artifact.SingleArtifact.APK)
|
||||||
|
val loader = variant.artifacts.getBuiltArtifactsLoader()
|
||||||
|
val packageName = projectConfig.packageName
|
||||||
|
|
||||||
|
val renameTask = tasks.register("rename${variant.name.replaceFirstChar { it.uppercase() }}Apk") {
|
||||||
|
inputs.files(apkFolder)
|
||||||
|
outputs.upToDateWhen { false }
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
val builtArtifacts = loader.load(apkFolder.get()) ?: return@doLast
|
||||||
|
|
||||||
|
builtArtifacts.elements.forEach { element ->
|
||||||
|
val apkFile = File(element.outputFile)
|
||||||
|
val outputFileName = "$packageName-v${element.versionName}-${element.versionCode}-$formattedVariantName.apk"
|
||||||
|
if (apkFile.exists() && apkFile.name != outputFileName) {
|
||||||
|
apkFile.copyTo(File(apkFile.parentFile, outputFileName), overwrite = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.matching { it.name == "assemble${variant.name.replaceFirstChar { it.uppercase() }}" }.configureEach {
|
||||||
|
finalizedBy(renameTask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
|
||||||
|
|
||||||
|
addBaseKotlin()
|
||||||
|
|
||||||
|
addDagger()
|
||||||
|
|
||||||
|
addOkio()
|
||||||
|
|
||||||
|
addBaseAndroid()
|
||||||
|
addBaseAndroidUi()
|
||||||
|
|
||||||
|
implementation("androidx.core:core-splashscreen:1.0.0-alpha02")
|
||||||
|
|
||||||
|
addNavigation()
|
||||||
|
|
||||||
|
addCompose()
|
||||||
|
addGlance()
|
||||||
|
addDataStore()
|
||||||
|
addNavigation3()
|
||||||
|
addSerialization()
|
||||||
|
|
||||||
|
addTesting()
|
||||||
|
|
||||||
|
"gplayImplementation"("com.android.billingclient:billing:8.0.0")
|
||||||
|
"gplayImplementation"("com.android.billingclient:billing-ktx:8.0.0")
|
||||||
|
|
||||||
|
"screenshotTestImplementation"(platform("androidx.compose:compose-bom:${Versions.Compose.bom}"))
|
||||||
|
"screenshotTestImplementation"("com.android.tools.screenshot:screenshot-validation-api:0.0.1-alpha13")
|
||||||
|
"screenshotTestImplementation"("androidx.compose.ui:ui-tooling")
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
-keepclassmembernames @com.squareup.moshi.JsonClass class * extends java.lang.Enum {
|
|
||||||
<fields>;
|
|
||||||
}
|
|
||||||
Vendored
+7
-22
@@ -1,23 +1,8 @@
|
|||||||
# Add project specific ProGuard rules here.
|
-keep class eu.darken.capod.BuildConfig { *; }
|
||||||
# You can control the set of applied configuration files using the
|
-dontobfuscate
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
# work-runtime 2.7.1 (pulled by Glance) uses Class.newInstance() reflection throughout.
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
# R8 full mode strips no-arg constructors not reachable by static analysis.
|
||||||
# class:
|
# Keep all constructors in the work package. Remove when Glance upgrades to work-runtime 2.10+.
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
# See: https://issuetracker.google.com/issues/243257364
|
||||||
# public *;
|
-keep class androidx.work.** { <init>(...); }
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
||||||
|
|
||||||
-dontobfuscate
|
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
package eu.darken.capod.screenshots
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import eu.darken.capod.R
|
||||||
|
import eu.darken.capod.common.compose.PreviewWrapper
|
||||||
|
import eu.darken.capod.main.ui.widget.ComposeWidgetPreview
|
||||||
|
import eu.darken.capod.main.ui.widget.WidgetRenderState
|
||||||
|
import eu.darken.capod.reaction.ui.popup.PopUpContent as PopUpCard
|
||||||
|
import eu.darken.capod.main.ui.widget.WidgetConfigurationScreen
|
||||||
|
import eu.darken.capod.main.ui.widget.WidgetConfigurationViewModel
|
||||||
|
import eu.darken.capod.main.ui.widget.WidgetTheme
|
||||||
|
import eu.darken.capod.common.compose.preview.MOCK_NOW
|
||||||
|
import eu.darken.capod.common.compose.preview.MockPodDataProvider
|
||||||
|
import eu.darken.capod.common.theming.CapodTheme
|
||||||
|
import eu.darken.capod.main.ui.overview.OverviewScreen
|
||||||
|
import eu.darken.capod.main.ui.overview.OverviewViewModel
|
||||||
|
import eu.darken.capod.main.ui.settings.SettingsScreen
|
||||||
|
import eu.darken.capod.main.ui.settings.SettingsViewModel
|
||||||
|
import eu.darken.capod.pods.core.PodDevice
|
||||||
|
import eu.darken.capod.profiles.ui.DeviceManagerScreen
|
||||||
|
import eu.darken.capod.profiles.ui.DeviceManagerViewModel
|
||||||
|
import eu.darken.capod.profiles.ui.creation.DeviceProfileCreationScreen
|
||||||
|
import eu.darken.capod.profiles.ui.creation.DeviceProfileCreationViewModel
|
||||||
|
import eu.darken.capod.reaction.core.autoconnect.AutoConnectCondition
|
||||||
|
import eu.darken.capod.reaction.ui.ReactionSettingsScreen
|
||||||
|
import eu.darken.capod.reaction.ui.ReactionSettingsViewModel
|
||||||
|
|
||||||
|
internal const val DS = "spec:width=1080px,height=2400px,dpi=428"
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun DashboardContent() = PreviewWrapper {
|
||||||
|
OverviewScreen(
|
||||||
|
state = OverviewViewModel.State(
|
||||||
|
now = MOCK_NOW,
|
||||||
|
permissions = emptySet(),
|
||||||
|
devices = listOf(
|
||||||
|
MockPodDataProvider.airPodsProMixed(),
|
||||||
|
MockPodDataProvider.airPodsMax(),
|
||||||
|
MockPodDataProvider.unknownDevice(),
|
||||||
|
),
|
||||||
|
isDebugMode = false,
|
||||||
|
isBluetoothEnabled = true,
|
||||||
|
profiles = listOf(
|
||||||
|
MockPodDataProvider.profile("My AirPods Pro", PodDevice.Model.AIRPODS_PRO2),
|
||||||
|
MockPodDataProvider.profile("AirPods Max", PodDevice.Model.AIRPODS_MAX),
|
||||||
|
),
|
||||||
|
upgradeInfo = MockPodDataProvider.gplayInfo(isPro = true),
|
||||||
|
showUnmatchedDevices = false,
|
||||||
|
),
|
||||||
|
onRequestPermission = {},
|
||||||
|
onManageDevices = {},
|
||||||
|
onSettings = {},
|
||||||
|
onUpgrade = {},
|
||||||
|
onToggleUnmatched = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun DeviceProfilesContent() = PreviewWrapper {
|
||||||
|
DeviceManagerScreen(
|
||||||
|
state = DeviceManagerViewModel.State(
|
||||||
|
profiles = listOf(
|
||||||
|
MockPodDataProvider.profile("Work AirPods", PodDevice.Model.AIRPODS_PRO2),
|
||||||
|
MockPodDataProvider.profile("AirPods Max", PodDevice.Model.AIRPODS_MAX),
|
||||||
|
MockPodDataProvider.profile("Gym Beats", PodDevice.Model.POWERBEATS_PRO),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onBack = {},
|
||||||
|
onAddDevice = {},
|
||||||
|
onEditProfile = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun AddProfileContent() = PreviewWrapper {
|
||||||
|
DeviceProfileCreationScreen(
|
||||||
|
state = DeviceProfileCreationViewModel.State(
|
||||||
|
isEditMode = false,
|
||||||
|
name = "",
|
||||||
|
nameError = null,
|
||||||
|
selectedModel = null,
|
||||||
|
availableModels = PodDevice.Model.entries.filter { it != PodDevice.Model.UNKNOWN },
|
||||||
|
identityKey = null,
|
||||||
|
encryptionKey = null,
|
||||||
|
selectedDevice = null,
|
||||||
|
bondedDevices = emptyList(),
|
||||||
|
minimumSignalQuality = 0.15f,
|
||||||
|
canSave = false,
|
||||||
|
),
|
||||||
|
onBack = {},
|
||||||
|
onSave = {},
|
||||||
|
onDelete = {},
|
||||||
|
onNameChange = {},
|
||||||
|
onModelChange = {},
|
||||||
|
onDeviceChange = {},
|
||||||
|
onIdentityKeyChange = {},
|
||||||
|
onEncryptionKeyChange = {},
|
||||||
|
onSignalQualityChange = {},
|
||||||
|
onKeyGuide = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun SettingsIndexContent() = PreviewWrapper {
|
||||||
|
SettingsScreen(
|
||||||
|
state = SettingsViewModel.State(isPro = true, sponsorUrl = "https://example.com"),
|
||||||
|
onNavigateUp = {},
|
||||||
|
onGeneralSettings = {},
|
||||||
|
onDeviceManager = {},
|
||||||
|
onReactions = {},
|
||||||
|
onSupport = {},
|
||||||
|
onWiki = {},
|
||||||
|
onChangelog = {},
|
||||||
|
onHelpTranslate = {},
|
||||||
|
onAcknowledgements = {},
|
||||||
|
onPrivacyPolicy = {},
|
||||||
|
onSponsor = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ReactionSettingsContent() = PreviewWrapper {
|
||||||
|
ReactionSettingsScreen(
|
||||||
|
state = ReactionSettingsViewModel.State(
|
||||||
|
isPro = true,
|
||||||
|
onePodMode = false,
|
||||||
|
autoPlay = true,
|
||||||
|
autoPause = true,
|
||||||
|
autoConnect = false,
|
||||||
|
autoConnectCondition = AutoConnectCondition.WHEN_SEEN,
|
||||||
|
showPopUpOnCaseOpen = true,
|
||||||
|
showPopUpOnConnection = false,
|
||||||
|
),
|
||||||
|
onNavigateUp = {},
|
||||||
|
onOnePodModeChanged = {},
|
||||||
|
onAutoPlayChanged = {},
|
||||||
|
onAutoPauseChanged = {},
|
||||||
|
onAutoConnectChanged = {},
|
||||||
|
onAutoConnectConditionSelected = {},
|
||||||
|
onShowPopUpOnCaseOpenChanged = {},
|
||||||
|
onShowPopUpOnConnectionChanged = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun WidgetConfigurationContent() = PreviewWrapper {
|
||||||
|
val profiles = listOf(
|
||||||
|
MockPodDataProvider.profile("My AirPods Pro", PodDevice.Model.AIRPODS_PRO2),
|
||||||
|
MockPodDataProvider.profile("AirPods Max", PodDevice.Model.AIRPODS_MAX),
|
||||||
|
)
|
||||||
|
WidgetConfigurationScreen(
|
||||||
|
state = WidgetConfigurationViewModel.State(
|
||||||
|
profiles = profiles,
|
||||||
|
selectedProfile = profiles.first().id,
|
||||||
|
isPro = true,
|
||||||
|
theme = WidgetTheme.DEFAULT,
|
||||||
|
activePreset = WidgetTheme.Preset.MATERIAL_YOU,
|
||||||
|
isCustomMode = false,
|
||||||
|
),
|
||||||
|
onSelectProfile = {},
|
||||||
|
onSelectPreset = {},
|
||||||
|
onEnterCustomMode = { _, _ -> },
|
||||||
|
onSetBackgroundColor = {},
|
||||||
|
onSetForegroundColor = {},
|
||||||
|
onSetBackgroundAlpha = {},
|
||||||
|
onSetShowDeviceLabel = {},
|
||||||
|
onReset = {},
|
||||||
|
onConfirm = {},
|
||||||
|
onCancel = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun CasePopUpContent() {
|
||||||
|
CapodTheme {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(
|
||||||
|
Brush.verticalGradient(
|
||||||
|
colors = listOf(
|
||||||
|
Color(0xFF1A237E),
|
||||||
|
Color(0xFF0D47A1),
|
||||||
|
Color(0xFF006064),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
PopUpCard(
|
||||||
|
device = MockPodDataProvider.airPodsProMixed(),
|
||||||
|
onClose = {},
|
||||||
|
modifier = Modifier.padding(horizontal = 32.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun HomescreenWidgetContent() {
|
||||||
|
CapodTheme {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(
|
||||||
|
Brush.verticalGradient(
|
||||||
|
colors = listOf(
|
||||||
|
Color(0xFF1A237E),
|
||||||
|
Color(0xFF0D47A1),
|
||||||
|
Color(0xFF006064),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
ComposeWidgetPreview(
|
||||||
|
state = WidgetRenderState.previewDualPod(
|
||||||
|
bgColor = MaterialTheme.colorScheme.surface.toArgb(),
|
||||||
|
textColor = MaterialTheme.colorScheme.onSurface.toArgb(),
|
||||||
|
iconColor = MaterialTheme.colorScheme.onSurface.toArgb(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(name = "1 - Dashboard Light", locale = "en", device = DS, showSystemUi = true)
|
||||||
|
@Composable
|
||||||
|
private fun PreviewDashboardLight() = DashboardContent()
|
||||||
|
|
||||||
|
@Preview(name = "2 - Dashboard Dark", locale = "en", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES, showSystemUi = true)
|
||||||
|
@Composable
|
||||||
|
private fun PreviewDashboardDark() = DashboardContent()
|
||||||
|
|
||||||
|
@Preview(name = "3 - Case Pop-up", device = DS, showSystemUi = true)
|
||||||
|
@Composable
|
||||||
|
private fun PreviewCasePopUp() = CasePopUpContent()
|
||||||
|
|
||||||
|
@Preview(name = "4 - Widget Configuration", locale = "en", device = DS, showSystemUi = true)
|
||||||
|
@Composable
|
||||||
|
private fun PreviewWidgetConfiguration() = WidgetConfigurationContent()
|
||||||
|
|
||||||
|
@Preview(name = "5 - Device Profiles", locale = "en", device = DS, showSystemUi = true)
|
||||||
|
@Composable
|
||||||
|
private fun PreviewDeviceProfiles() = DeviceProfilesContent()
|
||||||
|
|
||||||
|
@Preview(name = "6 - Add Profile", locale = "en", device = DS, showSystemUi = true)
|
||||||
|
@Composable
|
||||||
|
private fun PreviewAddProfile() = AddProfileContent()
|
||||||
|
|
||||||
|
@Preview(name = "7 - Settings", locale = "en", device = DS, showSystemUi = true)
|
||||||
|
@Composable
|
||||||
|
private fun PreviewSettingsIndex() = SettingsIndexContent()
|
||||||
|
|
||||||
|
@Preview(name = "8 - Reaction Settings", locale = "en", device = DS, showSystemUi = true)
|
||||||
|
@Composable
|
||||||
|
private fun PreviewReactionSettings() = ReactionSettingsContent()
|
||||||
|
|
||||||
|
@Preview(name = "9 - Homescreen Widget", device = DS, showSystemUi = true)
|
||||||
|
@Composable
|
||||||
|
private fun PreviewHomescreenWidget() = HomescreenWidgetContent()
|
||||||
@@ -1,24 +1,35 @@
|
|||||||
package eu.darken.capod.common.upgrade.core
|
package eu.darken.capod.common.upgrade.core
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.squareup.moshi.Moshi
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.preferences.SharedPreferencesMigration
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import eu.darken.capod.common.preferences.createFlowPreference
|
import eu.darken.capod.common.datastore.createValue
|
||||||
|
import eu.darken.capod.common.serialization.SerializationCapod
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class FossCache @Inject constructor(
|
class FossCache @Inject constructor(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
moshi: Moshi
|
@SerializationCapod json: Json,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val preferences = context.getSharedPreferences("settings_foss", Context.MODE_PRIVATE)
|
private val Context.dataStore by preferencesDataStore(
|
||||||
|
name = "settings_foss",
|
||||||
val upgrade = preferences.createFlowPreference<FossUpgrade?>(
|
produceMigrations = { ctx -> listOf(SharedPreferencesMigration(ctx, "settings_foss")) }
|
||||||
key = "foss.upgrade",
|
|
||||||
moshi = moshi,
|
|
||||||
defaultValue = null,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
private val dataStore: DataStore<Preferences> = context.dataStore
|
||||||
|
|
||||||
|
val upgrade = dataStore.createValue<FossUpgrade?>(
|
||||||
|
key = "foss.upgrade",
|
||||||
|
defaultValue = null,
|
||||||
|
json = json,
|
||||||
|
onErrorFallbackToDefault = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
package eu.darken.capod.common.upgrade.core
|
package eu.darken.capod.common.upgrade.core
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
import eu.darken.capod.common.serialization.InstantEpochMillisSerializer
|
||||||
import com.squareup.moshi.JsonClass
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@Serializable
|
||||||
data class FossUpgrade(
|
data class FossUpgrade(
|
||||||
val upgradedAt: Instant,
|
@SerialName("upgradedAt") @Serializable(with = InstantEpochMillisSerializer::class) val upgradedAt: Instant,
|
||||||
val reason: Reason
|
@SerialName("reason") val reason: Reason
|
||||||
) {
|
) {
|
||||||
@JsonClass(generateAdapter = false)
|
@Serializable
|
||||||
enum class Reason {
|
enum class Reason {
|
||||||
@Json(name = "foss.upgrade.reason.donated") DONATED,
|
@SerialName("foss.upgrade.reason.donated") DONATED,
|
||||||
@Json(name = "foss.upgrade.reason.alreadydonated") ALREADY_DONATED,
|
@SerialName("foss.upgrade.reason.alreadydonated") ALREADY_DONATED,
|
||||||
@Json(name = "foss.upgrade.reason.nomoney") NO_MONEY;
|
@SerialName("foss.upgrade.reason.nomoney") NO_MONEY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,16 @@
|
|||||||
package eu.darken.capod.common.upgrade.core
|
package eu.darken.capod.common.upgrade.core
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.widget.Toast
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import eu.darken.capod.R
|
|
||||||
import eu.darken.capod.common.WebpageTool
|
|
||||||
import eu.darken.capod.common.upgrade.UpgradeRepo
|
import eu.darken.capod.common.upgrade.UpgradeRepo
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
import eu.darken.capod.common.datastore.valueBlocking
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class UpgradeControlFoss @Inject constructor(
|
class UpgradeControlFoss @Inject constructor(
|
||||||
private val fossCache: FossCache,
|
private val fossCache: FossCache,
|
||||||
private val webpageTool: WebpageTool,
|
|
||||||
) : UpgradeRepo {
|
) : UpgradeRepo {
|
||||||
|
|
||||||
override val upgradeInfo: Flow<UpgradeRepo.Info> = fossCache.upgrade.flow.map { data ->
|
override val upgradeInfo: Flow<UpgradeRepo.Info> = fossCache.upgrade.flow.map { data ->
|
||||||
@@ -30,42 +25,22 @@ class UpgradeControlFoss @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launchBillingFlow(activity: Activity) {
|
fun upgrade(reason: FossUpgrade.Reason) {
|
||||||
MaterialAlertDialogBuilder(activity).apply {
|
fossCache.upgrade.valueBlocking = FossUpgrade(
|
||||||
setIcon(R.drawable.ic_heart)
|
upgradedAt = Instant.now(),
|
||||||
setTitle(R.string.upgrade_capod_label)
|
reason = reason
|
||||||
setMessage(R.string.upgrade_capod_description)
|
)
|
||||||
setPositiveButton(R.string.foss_upgrade_donate_label) { _, _ ->
|
|
||||||
fossCache.upgrade.value = FossUpgrade(
|
|
||||||
upgradedAt = Instant.now(),
|
|
||||||
reason = FossUpgrade.Reason.DONATED
|
|
||||||
)
|
|
||||||
webpageTool.open("https://github.com/d4rken/capod")
|
|
||||||
Toast.makeText(activity, R.string.general_thank_you_label, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
setNegativeButton(R.string.foss_upgrade_alreadydonated_label) { _, _ ->
|
|
||||||
fossCache.upgrade.value = FossUpgrade(
|
|
||||||
upgradedAt = Instant.now(),
|
|
||||||
reason = FossUpgrade.Reason.ALREADY_DONATED
|
|
||||||
)
|
|
||||||
Toast.makeText(activity, R.string.general_thank_you_label, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
setNeutralButton(R.string.foss_upgrade_no_money_label) { _, _ ->
|
|
||||||
fossCache.upgrade.value = FossUpgrade(
|
|
||||||
upgradedAt = Instant.now(),
|
|
||||||
reason = FossUpgrade.Reason.NO_MONEY
|
|
||||||
)
|
|
||||||
Toast.makeText(activity, "¯\\_(ツ)_/¯", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Info(
|
data class Info(
|
||||||
override val isPro: Boolean = false,
|
override val isPro: Boolean = false,
|
||||||
override val upgradedAt: Instant? = null,
|
override val upgradedAt: Instant? = null,
|
||||||
val upgradeReason: FossUpgrade.Reason? = null,
|
val upgradeReason: FossUpgrade.Reason? = null,
|
||||||
|
override val error: Throwable? = null,
|
||||||
) : UpgradeRepo.Info {
|
) : UpgradeRepo.Info {
|
||||||
override val type: UpgradeRepo.Type = UpgradeRepo.Type.FOSS
|
override val type: UpgradeRepo.Type = UpgradeRepo.Type.FOSS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getSponsorUrl(): String = "https://github.com/sponsors/d4rken"
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package eu.darken.capod.debug.autoreport
|
||||||
|
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import eu.darken.capod.common.debug.autoreport.AutomaticBugReporter
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
@Module
|
||||||
|
abstract class AutoReportingModule {
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
abstract fun autoreporting(foss: FossAutoReporting): AutomaticBugReporter
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package eu.darken.capod.debug.autoreport
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import eu.darken.capod.common.debug.autoreport.AutomaticBugReporter
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class FossAutoReporting @Inject constructor() : AutomaticBugReporter {
|
||||||
|
override fun setup(application: Application) {
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun notify(throwable: Throwable) {
|
||||||
|
throw IllegalStateException("Who initliazed this? Without setup no calls to here!")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package eu.darken.capod.upgrade.ui
|
||||||
|
|
||||||
|
import androidx.navigation3.runtime.EntryProviderScope
|
||||||
|
import androidx.navigation3.runtime.NavKey
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import dagger.multibindings.IntoSet
|
||||||
|
import eu.darken.capod.common.navigation.Nav
|
||||||
|
import eu.darken.capod.common.navigation.NavigationEntry
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class UpgradeNavigation @Inject constructor() : NavigationEntry {
|
||||||
|
override fun EntryProviderScope<NavKey>.setup() {
|
||||||
|
entry<Nav.Main.Upgrade> { UpgradeScreenHost() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
abstract class Mod {
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
abstract fun bind(entry: UpgradeNavigation): NavigationEntry
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
package eu.darken.capod.upgrade.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.systemBars
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.twotone.ArrowBack
|
||||||
|
import androidx.compose.material.icons.automirrored.twotone.Message
|
||||||
|
import androidx.compose.material.icons.twotone.BluetoothConnected
|
||||||
|
import androidx.compose.material.icons.twotone.Favorite
|
||||||
|
import androidx.compose.material.icons.twotone.Palette
|
||||||
|
import androidx.compose.material.icons.twotone.PlayCircle
|
||||||
|
import androidx.compose.material.icons.twotone.Widgets
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
|
import androidx.compose.ui.res.colorResource
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.withStyle
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
|
import eu.darken.capod.R
|
||||||
|
import eu.darken.capod.common.compose.Preview2
|
||||||
|
import eu.darken.capod.common.compose.PreviewWrapper
|
||||||
|
import eu.darken.capod.common.error.ErrorEventHandler
|
||||||
|
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun UpgradeScreenHost(vm: UpgradeViewModel = hiltViewModel()) {
|
||||||
|
ErrorEventHandler(vm)
|
||||||
|
NavigationEventHandler(vm)
|
||||||
|
|
||||||
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
val returnedEarlyMessage = stringResource(R.string.upgrade_foss_sponsor_returned_early)
|
||||||
|
|
||||||
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
|
DisposableEffect(lifecycleOwner) {
|
||||||
|
val observer = LifecycleEventObserver { _, event ->
|
||||||
|
if (event == Lifecycle.Event.ON_RESUME) {
|
||||||
|
vm.onResume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lifecycleOwner.lifecycle.addObserver(observer)
|
||||||
|
onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
vm.sponsorEvents.collect { event ->
|
||||||
|
when (event) {
|
||||||
|
UpgradeViewModel.SponsorEvent.ReturnedTooEarly -> {
|
||||||
|
snackbarHostState.showSnackbar(returnedEarlyMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpgradeScreen(
|
||||||
|
snackbarHostState = snackbarHostState,
|
||||||
|
onNavigateUp = { vm.navUp() },
|
||||||
|
onSponsor = { vm.sponsor() },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class Benefit(val icon: ImageVector, val textRes: Int)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun UpgradeScreen(
|
||||||
|
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
|
||||||
|
onNavigateUp: () -> Unit,
|
||||||
|
onSponsor: () -> Unit,
|
||||||
|
) {
|
||||||
|
val benefits = listOf(
|
||||||
|
Benefit(Icons.TwoTone.Palette, R.string.upgrade_benefit_themes),
|
||||||
|
Benefit(Icons.TwoTone.PlayCircle, R.string.upgrade_benefit_autoplay),
|
||||||
|
Benefit(Icons.TwoTone.BluetoothConnected, R.string.upgrade_benefit_autoconnect),
|
||||||
|
Benefit(Icons.AutoMirrored.TwoTone.Message, R.string.upgrade_benefit_popups),
|
||||||
|
Benefit(Icons.TwoTone.Widgets, R.string.upgrade_benefit_widgets),
|
||||||
|
Benefit(Icons.TwoTone.Favorite, R.string.upgrade_benefit_support),
|
||||||
|
)
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||||
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
|
) { paddingValues ->
|
||||||
|
Box(modifier = Modifier
|
||||||
|
.windowInsetsPadding(WindowInsets.systemBars)
|
||||||
|
.padding(paddingValues)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(horizontal = 24.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Box(contentAlignment = Alignment.Center) {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.size(120.dp),
|
||||||
|
shape = CircleShape,
|
||||||
|
color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.5f),
|
||||||
|
) {}
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.drawable.splash_graphic2),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(80.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = buildAnnotatedString {
|
||||||
|
append("CAPod ")
|
||||||
|
withStyle(SpanStyle(color = colorResource(R.color.brand_secondary), fontWeight = FontWeight.Bold)) {
|
||||||
|
append("FOSS")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style = MaterialTheme.typography.headlineLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
|
Card(
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
||||||
|
),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.upgrade_foss_preamble),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.padding(vertical = 4.dp)) {
|
||||||
|
benefits.forEach { benefit ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 12.dp, vertical = 4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
color = MaterialTheme.colorScheme.secondaryContainer,
|
||||||
|
modifier = Modifier.size(28.dp),
|
||||||
|
) {
|
||||||
|
Box(contentAlignment = Alignment.Center) {
|
||||||
|
Icon(
|
||||||
|
imageVector = benefit.icon,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||||
|
modifier = Modifier.size(16.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(benefit.textRes),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = onSponsor,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(52.dp),
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.TwoTone.Favorite,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(20.dp),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.upgrade_foss_sponsor_action),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.upgrade_foss_sponsor_subtitle),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
onClick = onNavigateUp,
|
||||||
|
modifier = Modifier.padding(4.dp),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.TwoTone.ArrowBack,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview2
|
||||||
|
@Composable
|
||||||
|
private fun UpgradeScreenPreview() = PreviewWrapper {
|
||||||
|
UpgradeScreen(
|
||||||
|
onNavigateUp = {},
|
||||||
|
onSponsor = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package eu.darken.capod.upgrade.ui
|
||||||
|
|
||||||
|
import android.os.SystemClock
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import eu.darken.capod.common.WebpageTool
|
||||||
|
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||||
|
import eu.darken.capod.common.flow.SingleEventFlow
|
||||||
|
import eu.darken.capod.common.uix.ViewModel4
|
||||||
|
import eu.darken.capod.common.upgrade.core.FossUpgrade
|
||||||
|
import eu.darken.capod.common.upgrade.core.UpgradeControlFoss
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class UpgradeViewModel @Inject constructor(
|
||||||
|
private val savedStateHandle: SavedStateHandle,
|
||||||
|
dispatcherProvider: DispatcherProvider,
|
||||||
|
private val upgradeControlFoss: UpgradeControlFoss,
|
||||||
|
private val webpageTool: WebpageTool,
|
||||||
|
) : ViewModel4(dispatcherProvider) {
|
||||||
|
|
||||||
|
sealed interface SponsorEvent {
|
||||||
|
data object ReturnedTooEarly : SponsorEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
val sponsorEvents = SingleEventFlow<SponsorEvent>()
|
||||||
|
|
||||||
|
fun sponsor() {
|
||||||
|
savedStateHandle[KEY_SPONSOR_OPENED_AT] = SystemClock.elapsedRealtime()
|
||||||
|
webpageTool.open("https://github.com/sponsors/d4rken")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onResume() {
|
||||||
|
val openedAt = savedStateHandle.get<Long>(KEY_SPONSOR_OPENED_AT) ?: return
|
||||||
|
savedStateHandle.remove<Long>(KEY_SPONSOR_OPENED_AT)
|
||||||
|
|
||||||
|
if (SystemClock.elapsedRealtime() - openedAt >= 5_000L) {
|
||||||
|
upgradeControlFoss.upgrade(FossUpgrade.Reason.DONATED)
|
||||||
|
navUp()
|
||||||
|
} else {
|
||||||
|
sponsorEvents.tryEmit(SponsorEvent.ReturnedTooEarly)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val KEY_SPONSOR_OPENED_AT = "sponsor_opened_at"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Skenk</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Ek het reeds geskenk</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Ek spandeer al my geld op AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS is gratis en oopbron. As u dit nuttig vind, oorweeg dit om ontwikkeling te befonds om die projek aan die gang te hou.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Borg ontwikkeling</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Geen advertensies. Geen opsporing. Geen Google Play-binding.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Alreeds terug? U ondersteuning hou CAPod aan die lewe.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">ልገሳ</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">አስቀድሜ ልገሳ ፈጽሜአለሁ</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">ገንዘቤን በሙሉ በAirPods ላይ አውጥቻለሁ</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS ነፃ እና ክፍት ምንጭ ነው። ግኾሚ ሆኖ ካግኝት፣ ፕሮጀክቱ ቀጥሎ እንደይይድ ለመርዳት ላማቱን ማስደገፏ ያስቡ።</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">ልማትን ይደግፉ</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">ምንም ማስታወቂያ የሉም። ምንም ክትትል የሉም። ምንም Google Play ቆለፋ የሉም።</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">ቀድሞ ተመለሱ፡ ድጋፏዎ CAPodን ህያው ያቆይላል።</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">تبرُّع</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">أنا متبرع بالفعل</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">أُنفق كل أموالي على AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS مجاني ومفتوح المصدر. إذا وجدته مفيداً، فكر في دعم التطوير للمساعدة في استمرار المشروع.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">ادعم التطوير</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">لا إعلانات. لا تتبع. لا قيود على Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">عدت بسرعة؟ دعمك يبقي CAPod حياً.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">İanə ver</string>
|
<string name="foss_upgrade_donate_label">İanə ver</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Artıq ianə vermişəm</string>
|
<string name="foss_upgrade_alreadydonated_label">Artıq ianə vermişəm</string>
|
||||||
<string name="foss_upgrade_no_money_label">Bütün pulumu AirPods-lara xərcləyirəm</string>
|
<string name="foss_upgrade_no_money_label">Bütün pulumu AirPods-lara xərcləyirəm</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS pulsuzdur və açıq mənbəlidir. Faydalı tapırsınızsa, layihənin davamına kömək etmək üçün inkişafı maliyyələşdirməyi düşünün.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">İnkişafı sponsor edin</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Reklam yoxdur. İzləmə yoxdur. Google Play bağlılığı yoxdur.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Artıq geri qayıtdınız? Dəstəyiniz CAPod-u yaşadır.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Ахвяраваць</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Я ўжо ахвяраваў</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Я патраціў усе грошы на AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS бясплатны і з адкрытым зыходным кодам. Калі вы лічыце яго карысным, разгледзьце магчымасць спансіравання распрацоўкі, каб дапамагчы трымаць праект ў жывых.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Падтрымаць распрацоўку</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Ніякай рэкламы. Ніякай адсочкі. Ніякай залежнасці ад Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Ужо вяртаецеся? Ваша падтрымка трымае CAPod ў жывых.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Дарение</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Вече съм дарил</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Харча всичките си пари за AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS е безплатен и с отворен код. Ако ви е полезен, размислете да подкрепите разработката, за да помогнете проектът да продължи.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Спонсорирайте разработката</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Без реклами. Без следене. Без заключване в Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Вече ли? Вашата подкрепа поддържа CAPod жив.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">দান করুন</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">আমি ইতিমধ্যে দান করেছি</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">আমি আমার সব টাকা AirPods এর জন্য খরচ করি</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS বিনামূল্যে এবং মুক্ত উৎস। আপনি যদি এটি উপযোগী মনে করেন, প্রকল্পটি চালু রাখতে সাহায্য করতে ডেভেলপমেন্ট স্পনসর করার কথা বিবেচনা করুন।</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">স্পনসর ডেভলপমেন্ট</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">কোনো বিজ্ঞাপন নেই। কোনো ট্র্যাকিং নেই। Google Play-এর সাথে বাঁধানো নেই।</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">ইতোমধ্যে ফিরে গেলেন? আপনার সমর্থন CAPodকে বাঁচিয়ে রাখে।</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">Donacions</string>
|
<string name="foss_upgrade_donate_label">Donacions</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Ja he donat</string>
|
<string name="foss_upgrade_alreadydonated_label">Ja he donat</string>
|
||||||
<string name="foss_upgrade_no_money_label">Gasto tots els meus diners en AirPods</string>
|
<string name="foss_upgrade_no_money_label">Gasto tots els meus diners en AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">El CAPod FOSS és gratuït i de codi obert. Si el trobeu útil, considereu patrocinar el desenvolupament per ajudar a mantenir el projecte en marxa.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Patrocina el desenvolupament</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Sense anuncis. Sense seguiment. Sense dependència del Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Ja heu tornat? El vostre suport manté viu el CAPod.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">Přispět</string>
|
<string name="foss_upgrade_donate_label">Přispět</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Již jsem přispěl/a</string>
|
<string name="foss_upgrade_alreadydonated_label">Již jsem přispěl/a</string>
|
||||||
<string name="foss_upgrade_no_money_label">Všechny peníze jsem utratil/a za AirPods</string>
|
<string name="foss_upgrade_no_money_label">Všechny peníze jsem utratil/a za AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">Aplikace CAPod FOSS je zdarma a open source. Pokud vám přijde užitečná, zvažte sponzorování vývoje, abyste pomohli udržet projekt v chodu.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Sponzorovat vývoj</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Žádné reklamy. Žádné sledování. Žádná závislost na Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Už jste zpět? Vaše podpora udržuje CAPod naživu.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Doner</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Jeg har allerede doneret</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Jeg bruger alle mine penge på AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS er gratis og open source. Hvis du finder det nüttigt, kan du overveje at sponsorere udviklingen for at hjælpe projektet med at fortsætte.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Sponsorudvikling</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Ingen annoncer. Ingen sporing. Ingen Google Play-lås.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Allerede tilbage? Din støtte holder CAPod i live.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -2,5 +2,9 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="foss_upgrade_donate_label">Spenden</string>
|
<string name="foss_upgrade_donate_label">Spenden</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Ich habe schon gespendet</string>
|
<string name="foss_upgrade_alreadydonated_label">Ich habe schon gespendet</string>
|
||||||
<string name="foss_upgrade_no_money_label">Ich gebe mein ganzes Geld für AirPods aus</string>
|
<string name="foss_upgrade_no_money_label">Hab alles für AirPods ausgegeben</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS ist kostenlos und Open Source. Wenn du es nützlich findest, erwäge, die Entwicklung zu sponsern, um das Projekt am Laufen zu halten.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Fördere Entwicklung</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Keine Werbung. Kein Tracking. Keine Google-Play-Bindung.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Schon zurück? Deine Unterstützung hält CAPod am Leben.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">Δωρεά</string>
|
<string name="foss_upgrade_donate_label">Δωρεά</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Ήδη δώρισα</string>
|
<string name="foss_upgrade_alreadydonated_label">Ήδη δώρισα</string>
|
||||||
<string name="foss_upgrade_no_money_label">Ξόδεψα όλα τα χρήματά μου στα AirPods</string>
|
<string name="foss_upgrade_no_money_label">Ξόδεψα όλα τα χρήματά μου στα AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">Το CAPod FOSS είναι δωρεάν και ανοιχτού κώδικα. Αν το βρίσκετε χρήσιμο, εξετάστε το ενδεχόμενο να χρηματοδοτήσετε την ανάπτυξη για να συνεχίσει το έργο.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Χορηγήστε την ανάπτυξη</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Χωρίς διαφημίσεις. Χωρίς παρακολούθηση. Χωρίς δέσμευση στο Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Επιστρέψατε ήδη; Η υποστήριξή σας κρατά το CAPod ζωντανό.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Donar</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Ya doné</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Gasto todo mi dinero en AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS es gratuito y de código abierto. Si lo encontrás útil, considerá patrocinar el desarrollo para ayudar a mantener el proyecto en marcha.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Sponsorear el desarrollo</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Sin publicidad. Sin rastreo. Sin dependencia de Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">¿Ya te vas? Tu apoyo mantiene CAPod vivo.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">Donar</string>
|
<string name="foss_upgrade_donate_label">Donar</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Ya doné</string>
|
<string name="foss_upgrade_alreadydonated_label">Ya doné</string>
|
||||||
<string name="foss_upgrade_no_money_label">Gasto todo mi dinero en AirPods</string>
|
<string name="foss_upgrade_no_money_label">Gasto todo mi dinero en AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS es gratuito y de código abierto. Si lo encuentras útil, considera patrocinar el desarrollo para ayudar a mantener el proyecto.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Patrocinar el desarrollo</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Sin anuncios. Sin rastreo. Sin dependencia de Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">¿Ya de vuelta? Tu apoyo mantiene CAPod con vida.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">Donar</string>
|
<string name="foss_upgrade_donate_label">Donar</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Ya he donado</string>
|
<string name="foss_upgrade_alreadydonated_label">Ya he donado</string>
|
||||||
<string name="foss_upgrade_no_money_label">Gasto todo mi dinero en AirPods</string>
|
<string name="foss_upgrade_no_money_label">Gasto todo mi dinero en AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS es gratuito y de código abierto. Si lo encuentras útil, considera patrocinar el desarrollo para ayudar a mantener el proyecto en marcha.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Patrocinadores</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Sin anuncios. Sin rastreo. Sin dependencia de Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">¿Ya de vuelta? Tu apoyo mantiene CAPod vivo.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Anneta</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Juba annetasin</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Kulutasin kogu raha AirPodsidele</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS on tasuta ja avatud lähtekoodiga. Kui sellest on sulle kasu, kaalu arenduse rahastamist, et aidata projekti arendada.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Rahasta arendamist</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Reklaami pole. Jälgimist pole. Google Play lukustust pole.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Juba tagasi? Sinu toetus hoiab CAPodi elus.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Eman dohaintza</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Dagoeneko eman dut dohaintza</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Nire diru guztia AirPods-etan gastatzen dut</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS doakoa eta kode irekikoa da. Erabilgarria iruditzen bazaizu, kontuan hartu garapena babestu proiektua aurrera jarraitzeko.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Garapena babestu</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Iragarkirik ez. Jarraipenik ez. Google Play-ren lotura ez.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Dagoeneko itzuli? Zure laguntzak CAPod bizirik mantentzen du.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">کمک مالی</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">کمک مالی کردهام</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">من تمام پولم را خرج ایرپاد میکنم</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS رایگان و متن-باز است. اگر آن را مفید یافتید، حمایت مالی از توسعه را در نظر بگیرید تا پروژه ادامه یابد.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">حامی توسعه دهنده</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">بدون تبلیغ. بدون ردیابی. بدون وابستگی به Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">زود برگشتیدی؟ حمایت شما CAPod را زنده نگه میدارد.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Lahjoita</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Olen jo lahjoittanut</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Käytän kaikki rahani AirPodeihin</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS on ilmainen ja avoimen lähdekoodin ohjelma. Jos löydät sen hyödylliseksi, harkitse kehityksen tukemista projektin elässä pitämiseksi.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Sponsoroi kehitystä</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Ei mainoksia. Ei seurantaa. Ei Google Play -riippuvuutta.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Jo takaisin? Tukesi pitää CAPod-sovelluksen hengissä.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Mag-donate</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Nakapag-donate na ako</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Ginagastos ko lahat ng pera ko sa AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">Ang CAPod FOSS ay libre at open source. Kung nakita mong kapaki-pakinabang ito, isaalang-alang ang pag-sponsor ng development para mapanatiling aktibo ang proyekto.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Mag-sponsor ng development</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Walang ads. Walang tracking. Walang Google Play lock-in.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Bumalik na? Ang iyong suporta ay nagpapanatiling buhay ng CAPod.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -2,5 +2,9 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="foss_upgrade_donate_label">Faire un don</string>
|
<string name="foss_upgrade_donate_label">Faire un don</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">J’ai déjà fait un don</string>
|
<string name="foss_upgrade_alreadydonated_label">J’ai déjà fait un don</string>
|
||||||
<string name="foss_upgrade_no_money_label">J’ai dépensé tout mon argent sur les AirPods</string>
|
<string name="foss_upgrade_no_money_label">J’ai dépensé tout mon argent sur des AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS est gratuite et à code source ouvert. Si vous la trouvez utile, pensez à soutenir le développement pour contribuer à la pérennité du projet.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Soutenir le développement</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Pas de publicités. Pas de suivi à la trace. Pas de dépendance à Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Déjà de retour ? Votre soutien maintient CAPod en vie.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Doar</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Xa doei</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Gasto todos os meus cartos en AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS é gratuito e de código aberto. Se o atopas útil, considera patrocinar o desenvolvemento para axudar a manter o proxecto en marcha.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Patrocinar desenvolvemento</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Sen anuncios. Sen rastrexo. Sen dependencia de Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">És rápido/a! O teu apoio mantén CAPod vivo.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="foss_upgrade_donate_label">दान करना</string>
|
<string name="foss_upgrade_donate_label">दान करें</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">मैंने पहले ही दान कर दिया है</string>
|
<string name="foss_upgrade_alreadydonated_label">मैंने पहले ही दान कर दिया है</string>
|
||||||
<string name="foss_upgrade_no_money_label">मैं अपना सारा पैसा AirPods पर खर्च करता हूं</string>
|
<string name="foss_upgrade_no_money_label">मैं अपना सारा पैसा AirPods पर खर्च करता हूं</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS मुफ्त और ओपन सोर्स है। यदि आपको यह उपयोगी लगता है, तो प्रोजेक्ट को जारी रखने में मदद के लिए विकास को प्रायोजित करने पर विचार करें।</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">विकास को प्रायोजक करें</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">कोई विज्ञापन नहीं। कोई ट्रैकिंग नहीं। Google Play पर निर्भरता नहीं।</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">अभी वापस? आपका समर्थन CAPod को जीवित रखता है।</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Donirajte</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Već sam donirao</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Sav svoj novac trošim na AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS je besplatan i otvorenog koda. Ako vam je koristan, razmislite o sponzoriranju razvoja kako bi projekt ostao živ.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Podržite razvoj</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Bez oglasa. Bez praćenja. Bez zaključanosti na Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Već se vraćate? Vaša podrška čuva CAPod na životu.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="foss_upgrade_donate_label">Adomány</string>
|
<string name="foss_upgrade_donate_label">Adományoz</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Már adományoztam</string>
|
<string name="foss_upgrade_alreadydonated_label">Már adományoztam</string>
|
||||||
<string name="foss_upgrade_no_money_label">Minden pénzemet AirPodokra költöm</string>
|
<string name="foss_upgrade_no_money_label">Minden pénzemet az AirPods-ra költöttem</string>
|
||||||
|
<string name="upgrade_foss_preamble">A CAPod FOSS ingyenes és nyílt forráskódú. Ha hasznosnak találod, fontold meg a fejlesztés támogatását, hogy a projekt tovább élhessen.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Szponzori fejlesztés</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Nincs hirdetés. Nincs nyomkövetés. Nincs Google Play-függőség.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Már visszamész? A támogatásodnak köszönhetően él tovább a CAPod.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Նվիրաբերել</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Ես արդեն նվիրաբերել եմ</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Ես իմ ամբողջ գումարը ծախսում եմ AirPods-ի վրա</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS անվճար և բաց կոդով: Այն ոգտակար էք, խնդրում զարգացությունը կենդանի պահելու համար:</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Հովանավորել զարգացումը</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Լրատնություն չկա: հետքխկություն չկա: Google Play-ի կապվածություն չկա:</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Արդևն հետ? ձևր աիկտակցությունը CAPod-ին կենդանի պահում:</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">Menyumbang</string>
|
<string name="foss_upgrade_donate_label">Menyumbang</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Saya telah berdonasi</string>
|
<string name="foss_upgrade_alreadydonated_label">Saya telah berdonasi</string>
|
||||||
<string name="foss_upgrade_no_money_label">Saya menghabiskan semua uang saya untuk AirPods</string>
|
<string name="foss_upgrade_no_money_label">Saya menghabiskan semua uang saya untuk AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS gratis dan sumber terbuka. Jika Anda merasa bermanfaat, pertimbangkan untuk mensponsori pengembangan guna menjaga proyek ini berjalan.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Pengembangan sponsor</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Tanpa iklan. Tanpa pelacakan. Tanpa ketergantungan Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Sudah kembali? Dukungan Anda menjaga CAPod tetap hidup.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Gefa</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Ég hef þegar gefið</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Ég eyði öllum peningunum mínum í AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS er ókeypis og opinn kóði. Ef þú finnur það gagnlegt, þakkaðu þér fyrir að stuðla að þróun til að hjálpa til við að halda verkefninu gangandi.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Styrkja þróun</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Engar auglysíngar. Engin rakning. Ekkert Google Play-lás.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Aftur þegar? Styðjað þít heldur CAPod lifandi.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">Dona</string>
|
<string name="foss_upgrade_donate_label">Dona</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Ho già donato</string>
|
<string name="foss_upgrade_alreadydonated_label">Ho già donato</string>
|
||||||
<string name="foss_upgrade_no_money_label">Spendo tutti i miei soldi per gli AirPods</string>
|
<string name="foss_upgrade_no_money_label">Spendo tutti i miei soldi per gli AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS è gratuito e open source. Se lo trovi utile, considera di sponsorizzare lo sviluppo per contribuire a mantenere il progetto in vita.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Finanzia lo sviluppo</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Nessuna pubblicità. Nessun tracciamento. Nessun vincolo con Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Già via? Il tuo supporto mantiene CAPod in vita.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">תרומה</string>
|
<string name="foss_upgrade_donate_label">תרומה</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">כבר תרמתי</string>
|
<string name="foss_upgrade_alreadydonated_label">כבר תרמתי</string>
|
||||||
<string name="foss_upgrade_no_money_label">מיטב כספי מושקע ב-AirPods</string>
|
<string name="foss_upgrade_no_money_label">מיטב כספי מושקע ב-AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS הוא חינם וקוד פתוח. אם הוא שימושי לך, שקול לשקול על חסוי הפיתוח כדי לעזור להמשיך את הפרויקט.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">פיתוח חסות</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">אין פרסומות. אין מעקב. אין תלות ב-Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">כבר חוזר? התמיכה שלך שומרת את CAPod בחיים.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">寄付</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">すでに寄付しました</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">AirPodsに全財産をつぎ込んでいます</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSSは無料かつオープンソースです。便利だと思ったら、プロジェクトの継続に向けて開発をスポンサーすることをご検討ください。</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">開発支援者</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">広告なし。追跡なし。Google Play依存なし。</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">もう戻りますか?あなたのサポートがCAPodを支えています。</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">შემოწირულობა</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">მე უკვე შემოწირული მაქვს</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">მთელ ჩემს ფულს AirPods-ებზე ვხარჯავ</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS უფასოა და ღია წყაროა. თუ გსარგებლია, გაანაბრეთ განვითარება პროექტის გადასაგრელად.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">სპონსორის განვითარება</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">რეკლამა არა. თვალთვალება არა. Google Play-ზე დამოკიდება არა.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">უკვე დაბრუნდით? თქვენი მხარდაჯერა CAPod-ს გადარჩენებას ეხმარება.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">បរិច្ចាគ</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">ខ្ញុំបានបរិច្ចាគរួចហើយ</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">ខ្ញុំចំណាយលុយទាំងអស់របស់ខ្ញុំលើ AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS ជាកម្មវិធីឥតគិតថ្លៃ និងប្រភពបើកចំហ។ ប្រសិនបើអ្នករកហើញថ្លៃវាមានប្រយោជន៍ សូមពិចារណាឧបត្ថម្ភការអភិវឌ្ឍន៍ ដើម្បីជួយឥ្សរិយគម្រោងបន្តដំណើរការ។</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">ឧបត្ថម្ភការអភិវឌ្ឍន៍</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">គ្មានការផ្សាយពាណិជ្ជកម្ម។ គ្មានការតាមដាន។ គ្មានការចាក់សោ Google Play។</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">ត្រលប់មកវិញហើយ។? ការគាំទ្ររបស់អ្នកចិញ្ចឹម CAPod ឥ្សរិយនៅរស់។</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Beşdar bike</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Min berê bexş kir</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Ez hemû pereyê xwe li AirPods xerc dikim</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS belaş û çavkaniya vekirî ye. Ger hûn wê kêrhastî dibînin, fikir bikin ku perkirezan bike da ku alîkariya berdewamî projeya bide.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Pêşdebiriyê teref bike</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Reklam tune. Śopandin tune. Girêdana Google Play tune.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Zirû vegeriya? Piştgiriya we CAPod sax dihêle.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">ದಾನ ಮಾಡಿ</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">ನಾನು ಈಗಾಗಲೇ ದಾನ ಮಾಡಿದ್ದೇನೆ</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">ನಾನು ನನ್ನ ಎಲ್ಲಾ ಹಣವನ್ನು AirPods ಗಾಗಿ ಖರ್ಚು ಮಾಡುತ್ತೇನೆ</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS ಉಚಿತ ಮತ್ತು ತೆರೆದ ಮೂಲದ್ದಾಗಿದೆ. ನಿಮಗೆ ಉಪಯುಕ್ತವಾಗಿ ತೋರಿದರೆ, ಯೋಜನೆಯನ್ನು ಮುಂದುವರಿಸಲು ಅಭಿವೃದ್ಧಿಗೆ ಪ್ರಾಯೋಜಕತ್ವ ನೀಡಲು ಪರಿಗಣಿಸಿ.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">ಅಭಿವೃದ್ಧಿಗೆ ಪ್ರಾಯೋಜಕತ್ವ ನೀಡಿ</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">ಜಾಹೀರಾತುಗಳಿಲ್ಲ. ಭಕ್ಷಣೆ ಇಲ್ಲ. Google Play ಲಾಕ್-ಇನ್ ಇಲ್ಲ.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">ಇಷ್ಟೇ ಹಿಂದಿರುಗಿದಿರಿ? ನಿಮ್ಮ ಬೆಂಬಲ CAPod ಅನ್ನು ಜೀವಂತವಾಗಿಡುತ್ತದೆ.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">후원하기</string>
|
<string name="foss_upgrade_donate_label">후원하기</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">이미 후원했어요</string>
|
<string name="foss_upgrade_alreadydonated_label">이미 후원했어요</string>
|
||||||
<string name="foss_upgrade_no_money_label">AirPods에 돈을 다 썼어요</string>
|
<string name="foss_upgrade_no_money_label">AirPods에 돈을 다 썼어요</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS는 무료 오픈소스입니다. 유용하게 사용하고 계신다면 프로젝트 유지에 도움이 되도록 후원을 고려해 주세요.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">개발 후원하기</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">광고 없음. 추적 없음. Google Play 의존 없음.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">벌써 돌아가세요? 여러분의 지원이 CAPod를 유지합니다.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Демөөрчүлүк кылуу</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Мен буга чейин демөөрчүлүк кылгам</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Мен бүт акчамды AirPods үчүн короттум</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS беплатан жана ачык каттулууда. Егер пайдалуу таапсаңыз, жобаны улантырууга колдоо көрсөтүңүз.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Иштелеп чыгууну колдоо</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Жарнама. Кадалоолоо жок. Google Playга байланма.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Артка кайттыңызбы? Сиздин колдооңуз CAPodты тирүү сактайт.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">ບໍລິຈາກ</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">ຂ້ອຍໄດ້ບໍລິຈາກແລ້ວ</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">ຂ້ອຍໃຊ້ເງິນທັງໝົດຂອງຂ້ອຍໃສ່ AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS ແມ່ນຟຣີ ແລະ ໂອເພນຊອດ. ຖ້າທ່ານເຫັນວ່າມີປະໂຫຍດ, ພິຈາລະນາສະໜັບສະໜູນການພັດທະນາເພື່ອຊ່ວຍໃຫ້ໂຄງການດຳເນີນຕໍ່ໄປ.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">ສະໜັບສະໜູນການພັດທະນາ</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">ບໍ່ມີໂຄສະນາ. ບໍ່ມີການຕິດຕາມ. ບໍ່ຕິດ Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">ກັບມາແລ້ວ? ການສະໜັບສະໜູນຂອງທ່ານຮັກສາ CAPod ໃຫ້ດຳເນີນຕໍ່ໄປ.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Aukoti</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Aš jau paaukojau</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Visus savo pinigus išleidžiu „AirPods“ ausinėms</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS yra nemokama ir atviro kodo programa. Jei ji jums naudinga, pagalvokite apie rėmimą, kad projektą būtų galima tęsti.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Remti plėtrą</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Jokių reklamų. Jokio sekimo. Jokio „Google Play“ priklausomybės.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Jau grįžte? Jūsų parama palaiko CAPod gyvybę.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Ziedot</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Es jau ziedoju</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Es iztērēju visu savu naudu AirPods austiņām</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS ir bezmaksas un atkļējkoda. Ja tā ir noderba, apsveriet attīstības sponsēšanu, lai palīdzētu turpināt projektu.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Sponsorēt attīstību</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Bez reklamām. Bez seklōšanas. Bez Google Play saistībām.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Jau atpakaļ? Jūsu atbalsts uztur CAPod pie dzīvības.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Донирај</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Веќе донирав</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Ги трошам сите пари на AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS е бесплатен и отворен код. Ако ви е корисен, размислете да го спонзорирате развојот за да помогнете проектот да продолжи.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Спонзорирај развој</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Без реклами. Без пратење. Без зависност од Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Веќе се вративте? Вашата поддршка го одржува CAPod жив.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">സംഭാവന ചെയ്യുക</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">ഞാൻ ഇതിനകം സംഭാവന ചെയ്തു</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">ഞാൻ എൻ്റെ എല്ലാ പണവും AirPods-നായി ചെലവഴിക്കുന്നു</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS സൗജന്യവും ഓപ്പൻ സോഴ്സുമാണ്. നിങ്ങൾക്ക് ഇത് ഉപകാരപ്രദമാണെങ്കിൽ, പ്രോജെക്ട് തുടരാൻ സഹായിക്കുന്നതിന് ഡെവലപ്പ്മെന്റ് സ്പോൺസർ ചെയ്യുന്നത് പരിഗണിക്കുക.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">വികസനം സ്പോൺസർ ചെയ്യുക</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">പരസ്യങ്ങൾ ഇല്ല. ട്രാക്കിംഗ് ഇല്ല. Google Play ലോക്ക്-ഇൻ ഇല്ല.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">ഇത്ര പെട്ടെന്ന് തിരിച്ചു പോകുന്നോ? നിങ്ങളുടെ പിന്തുണ CAPod നിലനിർത്തുന്നു.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Хандивлах</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Би аль хэдийн хандивласан</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Би бүх мөнгөө AirPods-д зарцуулдаг</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS нь үнэгүй, нээлтэй эхийн програм. Хэрэв танд хэрэгтэй бол төслийг үргэлжүүлэхэд туслахын тулд хөгжүүлэлтийг дэмжих талаар бодоорой.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Хөгжлийг дэмжих</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Реклам байхгүй. Хяналт байхгүй. Google Play-д хамааралгүй.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Аль хэдэйн буцаж ирлээ? Таны дэмжлэг CAPod-ийг амьд байлгадаг.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">दान करा</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">मी आधीच दान केले आहे</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">मी माझे सर्व पैसे AirPods वर खर्च करतो</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS विनामूल्य आणि ओपन सोर्स आहे. जर तुम्हाला ते उपयुक्त वाटत असेल, तर प्रकल्प सुरू ठेवण्यासाठी विकासास प्रायोजित करण्याचा विचार करा.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">विकासास प्रायोजित करा</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">कोणत्याही जाहिराती नाहीत. कोणतेही ट्रॅकिंग नाही. Google Play चे बंधन नाही.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">आधीच परत? तुमचा पाठिंबा CAPod जिवंत ठेवतो.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="foss_upgrade_donate_label">Derma</string>
|
<string name="foss_upgrade_donate_label">Sumbang</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Saya sudah menderma</string>
|
<string name="foss_upgrade_alreadydonated_label">Saya telah menyumbang</string>
|
||||||
<string name="foss_upgrade_no_money_label">Saya belanjakan semua wang saya untuk AirPods</string>
|
<string name="foss_upgrade_no_money_label">Saya belanjakan semua wang saya untuk AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS adalah percuma dan sumber terbuka. Jika anda mendapatinya berguna, pertimbangkan untuk menaja pembangunannya bagi membantu projek ini terus berjalan.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Pembangunan penaja</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Tiada iklan. Tiada penjejakan. Tiada ikatan Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Sudah kembali? Sokongan anda mengekalkan CAPod.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">လှူဒါန်းပါ</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">ကျွန်တော် လှူပြီးပါပြီ</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">ကျွန်တော် ပိုက်ဆံအားလုံးကို AirPods မှာ သုံးတယ်</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS သည် အခမဲ့နှင့် open source ဖြစ်သည်။ အသုံးပြုခုပ်ဆည်းနိုင် အသားဖြစ်ပါက ပရာဗက်ဆက်လက်ရန် ဖွဲ့ဖြီးရေးကို ပံ့ပိုးပေးသည်။</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">ဖွံ့ဖြိုးတိုးတက်မှုကို ကမကထပံ့ပိုးရန်</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">ကြောင်ညာမရှိ။ ခြေရာမခံ။ Google Play ချုပ်ချယ်မြုမရှိ။</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">ပြန်သွားပြီးလါ? သင်၏ ပံ့ပိုးမှု CAPod ကို ရှင်သန်ဆက်လက်ကေစေသည်။</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Doner</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Jeg har allerede donert</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Jeg bruker alle pengene mine på AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS er gratis og åpen kildekode. Hvis du synes den er nyttig, kan du vurdere å sponse utviklingen for å bidra til at prosjektet går videre.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Støtt utviklingen</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Ingen reklame. Ingen sporing. Ingen Google Play-avhengighet.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Allerede tilbake? Din støtte holder CAPod i live.</string>
|
||||||
|
</resources>
|
||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">दान गर्नुहोस्</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">मैले पहिले नै दान गरिसकेको छु</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">म आफ्नो सबै पैसा AirPods मा खर्च गर्छु</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS निःशुल्क र खुला स्रोत छ। यदि तपाईंले उपयोगी पाउनुभएको छ भने, प्राजेक्ट चलाउन राख्न विकासमा प्रायोजन गर्न विचार गर्नुहोस्।</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">विकासलाई प्रायोजन गर्नुहोस्</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">कुनै विज्ञापन छैन। कुनै ट्रैकिङ छैन। Google Playमा बाँधिएको छैन।</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">पहिले नै फिर्नुभयो? तपाईंको सहयोगले CAPod जीवित राख्छ।</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">Doneren</string>
|
<string name="foss_upgrade_donate_label">Doneren</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Ik heb al gedoneerd</string>
|
<string name="foss_upgrade_alreadydonated_label">Ik heb al gedoneerd</string>
|
||||||
<string name="foss_upgrade_no_money_label">Ik besteed al mijn geld aan AirPods</string>
|
<string name="foss_upgrade_no_money_label">Ik besteed al mijn geld aan AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS is gratis en open source. Als je het nuttig vindt, overweeg dan om de ontwikkeling te sponsoren om het project gaande te houden.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Sponsor ontwikkeling</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Geen advertenties. Geen tracking. Geen Google Play-vergrendeling.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Alweer terug? Jouw steun houdt CAPod in leven.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<string name="foss_upgrade_donate_label">Doner</string>
|
|
||||||
<string name="foss_upgrade_alreadydonated_label">Jeg har allerede donert</string>
|
|
||||||
<string name="foss_upgrade_no_money_label">Jeg bruker alle pengene mine på AirPods</string>
|
|
||||||
</resources>
|
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="foss_upgrade_donate_label">Dotacja</string>
|
<string name="foss_upgrade_donate_label">Wesprzyj</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Już się zrzuciłem</string>
|
<string name="foss_upgrade_alreadydonated_label">Już wsparłem</string>
|
||||||
<string name="foss_upgrade_no_money_label">Wydałem wszystkie pieniądze na AirPods</string>
|
<string name="foss_upgrade_no_money_label">Wydałem wszystkie pieniądze na AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS jest bezpłatny i otwartoźródłowy. Jeśli uważasz go za użyteczny, rozważ sponsorowanie rozwoju, aby pomóc kontynuować projekt.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Wesprzyj rozwój</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Bez reklam. Bez śledzenia. Bez uzależnienia od Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Już wróciłeś? Twoje wsparcie utrzymuje CAPod przy życiu.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">Doar</string>
|
<string name="foss_upgrade_donate_label">Doar</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Eu já doei</string>
|
<string name="foss_upgrade_alreadydonated_label">Eu já doei</string>
|
||||||
<string name="foss_upgrade_no_money_label">Eu gasto todo o meu dinheiro em AirPods</string>
|
<string name="foss_upgrade_no_money_label">Eu gasto todo o meu dinheiro em AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">O CAPod FOSS é gratuito e de código aberto. Se você o achar útil, considere patrocinar o desenvolvimento para ajudar a manter o projeto ativo.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Patrocinador do desenvolvimento</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Sem anúncios. Sem rastreamento. Sem dependência do Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Já voltou? Seu apoio mantém o CAPod vivo.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Doar</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Já doei</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Gastei todo o meu dinheiro em AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">O CAPod FOSS é gratuito e de código aberto. Se o achar útil, considere patrocinar o desenvolvimento para ajudar a manter o projeto ativo.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Patrocine o desenvolvimento</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Sem anúncios. Sem rastreamento. Sem dependência do Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Já está a sair? O seu apoio mantém o CAPod ativo.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Donar</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Hai gia donat</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Mett tut mes daners en AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS è gratuit ed open source. Sche ti chattast ch\'el è util, considerescha da sponsurisar il sviluppament per agidà a mantegnair il project.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Sponsurisar il sviluppament</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Nagins annunzis. Nagin tracking. Nagin blocadi da Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Gia enavos? Tes sustegn mantegn CAPod viv.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Donează</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Am donat deja</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Mi-am cheltuit toți banii pe AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS este gratuit si open source. Daca il gasiti util, luati in considerare sponsorizarea dezvoltarii pentru a ajuta la continuarea proiectului.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Sponsorizează dezvoltarea</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Fara reclame. Fara urmarire. Fara dependenta de Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Ati revenit deja? Sprijinul dvs. mentine CAPod in viata.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -3,4 +3,8 @@
|
|||||||
<string name="foss_upgrade_donate_label">Пожертвовать</string>
|
<string name="foss_upgrade_donate_label">Пожертвовать</string>
|
||||||
<string name="foss_upgrade_alreadydonated_label">Я уже пожертвовал</string>
|
<string name="foss_upgrade_alreadydonated_label">Я уже пожертвовал</string>
|
||||||
<string name="foss_upgrade_no_money_label">Я потратил все свои деньги на AirPods</string>
|
<string name="foss_upgrade_no_money_label">Я потратил все свои деньги на AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS бесплатный и с открытым исходным кодом. Если вам полезно приложение, рассмотрите возможность спонсировать разработку, чтобы помочь проекту продолжать развиваться.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Поддержать разработку</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Без рекламы. Без слежки. Без привязки к Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Вернулись уже? Ваша поддержка помогает CAPod жить.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="foss_upgrade_donate_label">Dona</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">Apo giai donadu</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">Apo spèndidu totu is soddos meos pro is AirPods</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS est gratis e fonte aberta. Si t’est de agiudu, considera de patrotzinare su isviluppu pro agiudare a mantennere su progetu.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">Sponsoriza s\'isvilupu</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">Peroe publicidade. Peroe trachiamentu. Peroe dipendentzia de Google Play.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">Zas torradas giai? Su sostegnu tuyu mantenet CAPod in vida.</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="foss_upgrade_donate_label">පරිත්යාග</string>
|
<string name="foss_upgrade_donate_label">පරිත්යාග</string>
|
||||||
|
<string name="foss_upgrade_alreadydonated_label">මම දැනටමත් පරිත්යාග කර ඇත</string>
|
||||||
|
<string name="foss_upgrade_no_money_label">මම මගේ මුදල් සියල්ල AirPods සඳහා වියදම් කරමි</string>
|
||||||
|
<string name="upgrade_foss_preamble">CAPod FOSS නියමිත සහ විවෘත මූලාශ්රයි. ඔබට ඉත උපකාරී නම්, ආයෝජනය ගිණුම් සහය ගෙවිපීම කිරීමට අර්වමයක් ගෙවිපීම් ප්රස්තා කරන්නට සංවර්ධන කරන්න.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_action">සංවර්ධනයට අනුග්රහය දෙන්න</string>
|
||||||
|
<string name="upgrade_foss_sponsor_subtitle">අළු නැත. ට්රැකිං නැත. Google Play සලකත්වය නැත.</string>
|
||||||
|
<string name="upgrade_foss_sponsor_returned_early">එක්බේන්මට පළ්ට? ඔබ෪් සහය් CAPod ක්රියාත්මකව තබා ගෙනයි.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user