Compare commits
3063 Commits
v0.22.10
...
rtkit-real
Author | SHA1 | Date | |
---|---|---|---|
3a02588fbf
|
|||
27d430d9cf
|
|||
b84ffa265c
|
|||
![]() |
af649ae85f | ||
![]() |
30e1cbbcba | ||
![]() |
86ab4bc62c | ||
![]() |
a34bf3588e | ||
![]() |
64c9e5a2d4 | ||
![]() |
9f02a88d19 | ||
![]() |
e6a25e7e0b | ||
![]() |
f6337dd95e | ||
![]() |
42fc26cc6d | ||
![]() |
1b6567b31b | ||
![]() |
171bb9b737 | ||
![]() |
3626d4ec9a | ||
![]() |
71c8f4de40 | ||
![]() |
1bd204527e | ||
![]() |
9b25d59120 | ||
![]() |
ca5580a560 | ||
![]() |
dd59db2be2 | ||
![]() |
c290e0b965 | ||
![]() |
689d231809 | ||
![]() |
84ff3c6a0d | ||
![]() |
093122aaeb | ||
![]() |
4f981adb97 | ||
![]() |
3945b808a2 | ||
![]() |
a171a588cf | ||
![]() |
2388f38d7d | ||
![]() |
dac2c4df9b | ||
![]() |
843cb1604d | ||
![]() |
59c289c7d8 | ||
![]() |
4b85bb57cc | ||
![]() |
a19b6919e0 | ||
![]() |
1caeb9b418 | ||
![]() |
d3717c5b81 | ||
![]() |
90987194cb | ||
![]() |
2953737478 | ||
![]() |
ac3a5e27ec | ||
![]() |
4ae89b7b4a | ||
![]() |
568494440f | ||
![]() |
5d0aeda4e9 | ||
![]() |
d89b9d1683 | ||
![]() |
c7c4e7a6bf | ||
![]() |
1c7c2026db | ||
![]() |
a9805db500 | ||
![]() |
e8a4317f31 | ||
![]() |
f4f5e94a36 | ||
![]() |
b2326b9a98 | ||
![]() |
1dc8cd8eef | ||
![]() |
147fe18ad6 | ||
![]() |
6fd6a5110c | ||
![]() |
e8ce417150 | ||
![]() |
da40483666 | ||
![]() |
06bc373ace | ||
![]() |
7797158ea6 | ||
![]() |
01a04baf7b | ||
![]() |
46b461df42 | ||
![]() |
898e0a2bc4 | ||
![]() |
2d3271859f | ||
![]() |
b6672004bc | ||
![]() |
eafca183a6 | ||
![]() |
59f9e0ca70 | ||
![]() |
800a03f0dc | ||
![]() |
55c11448ff | ||
![]() |
e9c1ea684b | ||
![]() |
103487e8ad | ||
![]() |
fb59bbc481 | ||
![]() |
d0f6aa5b87 | ||
![]() |
fdb8874803 | ||
![]() |
c9a22b3404 | ||
![]() |
fc5d10b776 | ||
![]() |
4cefb30dd9 | ||
![]() |
36482f5e6a | ||
![]() |
d822685c53 | ||
![]() |
9d87fb5e09 | ||
![]() |
85014b5fa2 | ||
![]() |
aff929dbd6 | ||
![]() |
dda85e02bf | ||
![]() |
5b393052ee | ||
![]() |
624da8ce5b | ||
![]() |
bca9e3e347 | ||
![]() |
2276ebd70f | ||
![]() |
a9bee1c64b | ||
![]() |
c8e88408b2 | ||
![]() |
45644759fe | ||
![]() |
e014d31972 | ||
![]() |
b7655d38f7 | ||
![]() |
268f737ac4 | ||
![]() |
80ff0a062a | ||
![]() |
24bcf44a47 | ||
![]() |
f1e43cb498 | ||
![]() |
ae1c5e3424 | ||
![]() |
9eb5879542 | ||
![]() |
038759506f | ||
![]() |
2bf9fdf10e | ||
![]() |
0bf77f4eb3 | ||
![]() |
034bcf4f44 | ||
![]() |
834d6dcf46 | ||
![]() |
8a642c8a83 | ||
![]() |
fe42ad2439 | ||
![]() |
51242be72b | ||
![]() |
cb62aff43e | ||
![]() |
491e726540 | ||
![]() |
7404be41dc | ||
![]() |
b8289ae923 | ||
![]() |
6b92b7adb9 | ||
![]() |
00a352ffcd | ||
![]() |
63cc07b8a7 | ||
![]() |
b2bf95009b | ||
![]() |
7df041310c | ||
![]() |
cd9522c1d1 | ||
![]() |
59bd0fe0f0 | ||
![]() |
0872a761c8 | ||
![]() |
996e158f56 | ||
![]() |
7fb195bc0b | ||
![]() |
2fabaa2e95 | ||
![]() |
e3cf9bb0a1 | ||
![]() |
407db96d4a | ||
![]() |
3689200b78 | ||
![]() |
6c16430a85 | ||
![]() |
ff37d11610 | ||
![]() |
509786cbf1 | ||
![]() |
148050898a | ||
![]() |
a29d3eb975 | ||
![]() |
1247ee5347 | ||
![]() |
f15b6a43d3 | ||
![]() |
bddfff9c2b | ||
![]() |
cdc99812c9 | ||
![]() |
63ad12bb89 | ||
![]() |
7c4ddb5943 | ||
![]() |
3cf5354e3f | ||
![]() |
838398103c | ||
![]() |
30bd70939a | ||
![]() |
275cd9d1d0 | ||
![]() |
5b001957c7 | ||
![]() |
d1e5c90c3b | ||
![]() |
715ef846b6 | ||
![]() |
aee49d1c1c | ||
![]() |
459390cd56 | ||
![]() |
7ca8dedb35 | ||
![]() |
f6cee35896 | ||
![]() |
66ee03741d | ||
![]() |
b49cfe96f4 | ||
![]() |
ae112fe077 | ||
![]() |
a1cbfa1623 | ||
![]() |
e06d775af5 | ||
![]() |
950f5f4d32 | ||
![]() |
abc8420697 | ||
![]() |
e309941646 | ||
![]() |
ea2ced6b9f | ||
![]() |
f1d06396a7 | ||
![]() |
95c0e2505c | ||
![]() |
7adda0aa66 | ||
![]() |
a0825e6ce0 | ||
![]() |
c7621ec0e4 | ||
![]() |
2c7ca16c4e | ||
![]() |
ad8c2577c4 | ||
![]() |
7c9a460786 | ||
![]() |
c644b7616a | ||
![]() |
badf7101e2 | ||
![]() |
6fb91e661c | ||
![]() |
ed819a05e3 | ||
![]() |
2e3a51a5da | ||
![]() |
e682940c54 | ||
![]() |
fa375cbaeb | ||
![]() |
3f638bfa03 | ||
![]() |
0ba0c64093 | ||
![]() |
5e107c33d9 | ||
![]() |
4bb379a218 | ||
![]() |
3710b54d43 | ||
![]() |
70f1f9cff8 | ||
![]() |
9787d39c3f | ||
![]() |
54527068d5 | ||
![]() |
0186f73c7a | ||
![]() |
88594c81d6 | ||
![]() |
f6bd49ba61 | ||
![]() |
a404e5754e | ||
![]() |
b080ca8627 | ||
![]() |
0e8cd3b961 | ||
![]() |
8fcb6e148f | ||
![]() |
ce9ee38304 | ||
![]() |
f9d47502d8 | ||
![]() |
f15014b6af | ||
![]() |
2a9c3a2e50 | ||
![]() |
3e9c2cce71 | ||
![]() |
93bf99f639 | ||
![]() |
c0a9434f34 | ||
![]() |
d7212624b0 | ||
![]() |
70a0a781c8 | ||
![]() |
ab011adf77 | ||
![]() |
8ea3f86f43 | ||
![]() |
bd78307940 | ||
![]() |
3cc7b7dbf9 | ||
![]() |
57e7fb3f62 | ||
![]() |
687475cf3c | ||
![]() |
c48dbd5dd4 | ||
![]() |
37049aab36 | ||
![]() |
18495fbb4e | ||
![]() |
cdcee16738 | ||
![]() |
83c2d9c964 | ||
![]() |
074a041760 | ||
![]() |
083d39ea80 | ||
![]() |
a5da7fd51a | ||
![]() |
cf9a2eb508 | ||
![]() |
0aeda01ba6 | ||
![]() |
3798d11a8d | ||
![]() |
fab03f800a | ||
![]() |
5b5b101c56 | ||
![]() |
7770accee0 | ||
![]() |
eb3cd7bed3 | ||
![]() |
56cc2f4c65 | ||
![]() |
9723af3f35 | ||
![]() |
dcf3cf4216 | ||
![]() |
578c94081f | ||
![]() |
efa8304d2d | ||
![]() |
2752f67877 | ||
![]() |
f55e0df614 | ||
![]() |
177ffde90f | ||
![]() |
0d1fb9a02c | ||
![]() |
a0a39ae828 | ||
![]() |
e57c60e3d8 | ||
![]() |
53090e3745 | ||
![]() |
93eb3da48a | ||
![]() |
75a3d9340e | ||
![]() |
9445ea1565 | ||
![]() |
a5a26d304b | ||
![]() |
1d8d298a22 | ||
![]() |
508bfbe334 | ||
![]() |
1385212572 | ||
![]() |
8dcd6ea2b4 | ||
![]() |
75d0914373 | ||
![]() |
35dc1fc589 | ||
![]() |
765a6a2f20 | ||
![]() |
131263cbe3 | ||
![]() |
936611b47c | ||
![]() |
266eb393eb | ||
![]() |
194ecd69e0 | ||
![]() |
b24b3c1054 | ||
![]() |
5de0909ae5 | ||
![]() |
da6efd6361 | ||
![]() |
9db7144d0f | ||
![]() |
bd36b5e310 | ||
![]() |
015870ac71 | ||
![]() |
b1677bf79c | ||
![]() |
1078c1c1bf | ||
![]() |
9a8579d956 | ||
![]() |
b6e187efd8 | ||
![]() |
b5bd294e5c | ||
![]() |
ac60bd47f0 | ||
![]() |
b7248f0333 | ||
![]() |
8a5b5378e6 | ||
![]() |
4a49f75799 | ||
![]() |
9d2666f293 | ||
![]() |
e63fcc5982 | ||
![]() |
4c37c17f2e | ||
![]() |
722820a375 | ||
![]() |
688023eb9e | ||
![]() |
5771aeaddd | ||
![]() |
a42da90042 | ||
![]() |
d7d32ed6fc | ||
![]() |
4715acf27e | ||
![]() |
8780db5ee8 | ||
![]() |
30d2a31473 | ||
![]() |
9a7a8ec137 | ||
![]() |
88fa68f030 | ||
![]() |
78e643150f | ||
![]() |
eb1463fd7c | ||
![]() |
6dce8dcaa5 | ||
![]() |
fc4a579d83 | ||
![]() |
263b9916c2 | ||
![]() |
16604997d6 | ||
![]() |
77226bc0d8 | ||
![]() |
7a6672b7ed | ||
![]() |
8db14c9cb3 | ||
![]() |
370df37596 | ||
![]() |
84e3501084 | ||
![]() |
a7d41a99a1 | ||
![]() |
c9c5e84119 | ||
![]() |
8a3b48754e | ||
![]() |
849c4012c0 | ||
![]() |
d8bb833ba3 | ||
![]() |
4d8a2ea688 | ||
![]() |
a7a61e9e41 | ||
![]() |
415cf096e9 | ||
![]() |
7774c3369e | ||
![]() |
bcab29d53a | ||
![]() |
d6195025fb | ||
![]() |
d7ae512b5e | ||
![]() |
b7b4c6b4ea | ||
![]() |
f59f17013d | ||
![]() |
4beedec3ad | ||
![]() |
950888d161 | ||
![]() |
76562fc093 | ||
![]() |
0676ab7f47 | ||
![]() |
fc5d258890 | ||
![]() |
7dce7ad32d | ||
![]() |
fa29693550 | ||
![]() |
2c881c63ba | ||
![]() |
fd7d251358 | ||
![]() |
acb9ee9792 | ||
![]() |
3d99f1d8a6 | ||
![]() |
2ef36af68d | ||
![]() |
d7b4634e5e | ||
![]() |
2bc57c38d2 | ||
![]() |
744308bfff | ||
![]() |
7db93fb20b | ||
![]() |
95db2db736 | ||
![]() |
f807345022 | ||
![]() |
b745d1f226 | ||
![]() |
52e2130d51 | ||
![]() |
1930ed16cb | ||
![]() |
1a063fee7e | ||
![]() |
51e0fefda7 | ||
![]() |
449f8af7d0 | ||
![]() |
38b46e680a | ||
![]() |
17d0cd0abd | ||
![]() |
98bc63251e | ||
![]() |
030bac441f | ||
![]() |
f82a385be2 | ||
![]() |
052719c0e4 | ||
![]() |
bfbff31860 | ||
![]() |
b1067333dc | ||
![]() |
35bdf74952 | ||
![]() |
23c2bba483 | ||
![]() |
25e8ce2d37 | ||
![]() |
124c0e66ee | ||
![]() |
202bb4f90f | ||
![]() |
5ee42ada67 | ||
![]() |
752c6c1f03 | ||
![]() |
def2221dfd | ||
![]() |
9ff8e02e54 | ||
![]() |
32dd9704ce | ||
![]() |
523519182a | ||
![]() |
3f718e8924 | ||
![]() |
26c4211cd4 | ||
![]() |
a3a07280e8 | ||
![]() |
ed755f04e9 | ||
![]() |
2809db8b40 | ||
![]() |
c866199d4c | ||
![]() |
39937be2e1 | ||
![]() |
d207c144d6 | ||
![]() |
435f1eb6cc | ||
![]() |
c7b80b0828 | ||
![]() |
59199b5620 | ||
![]() |
ebfc83dac5 | ||
![]() |
f535ccf9bf | ||
![]() |
75eb2c257c | ||
![]() |
314667259e | ||
![]() |
dbb2b29271 | ||
![]() |
7f25ede888 | ||
![]() |
4a2fff019a | ||
![]() |
fae5d16d43 | ||
![]() |
7bc2259b35 | ||
![]() |
f66aed921c | ||
![]() |
6db4b818e6 | ||
![]() |
f94caa96b8 | ||
![]() |
965c466e9b | ||
![]() |
731f20111a | ||
![]() |
d62f7cdc34 | ||
![]() |
f7790430a0 | ||
![]() |
959826d1d1 | ||
![]() |
ec30716e01 | ||
![]() |
dc51015c75 | ||
![]() |
72b0eeb7b1 | ||
![]() |
b050e0132e | ||
![]() |
cf962d94c7 | ||
![]() |
c29d23b4c3 | ||
![]() |
61e8df913d | ||
![]() |
4a55e3e8bd | ||
![]() |
2e78bd430c | ||
![]() |
dbaa72cb40 | ||
![]() |
596d2d93dd | ||
![]() |
10311b3a65 | ||
![]() |
332ba42073 | ||
![]() |
4262d29965 | ||
![]() |
0899d80ae0 | ||
![]() |
9fe6493fc2 | ||
![]() |
902cb8efac | ||
![]() |
f54210bf68 | ||
![]() |
fb151c8662 | ||
![]() |
323517753d | ||
![]() |
5726d9f06a | ||
![]() |
0992fab1c8 | ||
![]() |
b546ddc54c | ||
![]() |
d45b7cc972 | ||
![]() |
12eaaef210 | ||
![]() |
5b8ef9a62b | ||
![]() |
196258ea13 | ||
![]() |
0a035f3ce0 | ||
![]() |
7ab789fbaf | ||
![]() |
9c8f4aaf99 | ||
![]() |
3bef4f839a | ||
![]() |
1dfda3a5e1 | ||
![]() |
9d9c4045e2 | ||
![]() |
d466deedad | ||
![]() |
bbbbf5f4bd | ||
![]() |
f030b22bec | ||
![]() |
a693052f2c | ||
![]() |
e3809bd4f0 | ||
![]() |
41cc695848 | ||
![]() |
3558317dc9 | ||
![]() |
18cb34825d | ||
![]() |
4c4a3c8664 | ||
![]() |
b4374ddb35 | ||
![]() |
d24e7763f6 | ||
![]() |
49ad4e9f3c | ||
![]() |
ea96b321dc | ||
![]() |
bc758cece0 | ||
![]() |
0e4885c1fa | ||
![]() |
9944c97203 | ||
![]() |
58a5550439 | ||
![]() |
8e42467bd2 | ||
![]() |
e00e7802c8 | ||
![]() |
55d0bbade2 | ||
![]() |
de9f0dc910 | ||
![]() |
cf5970a6e1 | ||
![]() |
29747a213f | ||
![]() |
c64b4838dc | ||
![]() |
040c4a8560 | ||
![]() |
90dfa437e0 | ||
![]() |
4486b2eded | ||
![]() |
3db8a4f41b | ||
![]() |
9704cf3dcb | ||
![]() |
9aa6b03ba8 | ||
![]() |
45f92f0ef0 | ||
![]() |
23a3278b4e | ||
![]() |
951f916440 | ||
![]() |
fb87e19bae | ||
![]() |
49edb16de0 | ||
![]() |
8671896e4c | ||
![]() |
c2470ebd9c | ||
![]() |
a27fb71c4c | ||
![]() |
f6a687dc2b | ||
![]() |
9210705598 | ||
![]() |
3d995bba5f | ||
![]() |
c77f5095d6 | ||
![]() |
3701378449 | ||
![]() |
cb9f3c1a5b | ||
![]() |
fe66cde616 | ||
![]() |
0cc9ef0aee | ||
![]() |
68a424d9e1 | ||
![]() |
13576b8a2e | ||
![]() |
7c21d57953 | ||
![]() |
1e9e182a32 | ||
![]() |
3733bc57b7 | ||
![]() |
a196d1ddf2 | ||
![]() |
8861279add | ||
![]() |
f2f6dc5897 | ||
![]() |
00c4abfd2e | ||
![]() |
2d1feb78b5 | ||
![]() |
266deaaef1 | ||
![]() |
d2382e8de9 | ||
![]() |
c39d8e5813 | ||
![]() |
bd59c889f3 | ||
![]() |
553c2e9e2b | ||
![]() |
bdc5602244 | ||
![]() |
3229da48e3 | ||
![]() |
e380ae90eb | ||
![]() |
8d15e0fed3 | ||
![]() |
b459fd3910 | ||
![]() |
8790f2469c | ||
![]() |
d3ef4ab234 | ||
![]() |
fcddab84c6 | ||
![]() |
28c030c0f0 | ||
![]() |
175103e8d4 | ||
![]() |
3f638eeed6 | ||
![]() |
df2be0a75f | ||
![]() |
608c2a8c93 | ||
![]() |
2d918bf2c9 | ||
![]() |
0bd1289371 | ||
![]() |
b2e2d36d79 | ||
![]() |
11bb2ad0fd | ||
![]() |
f37b699349 | ||
![]() |
044280c538 | ||
![]() |
473fb4e19f | ||
![]() |
404e1c3912 | ||
![]() |
3648475f87 | ||
![]() |
1402869715 | ||
![]() |
a64ea486e8 | ||
![]() |
ffa917e9b5 | ||
![]() |
ac1265b9cc | ||
![]() |
2ce8310448 | ||
![]() |
a739eefb01 | ||
![]() |
432bfa15f4 | ||
![]() |
7b938b4d14 | ||
![]() |
abb23ba894 | ||
![]() |
cd64f9c422 | ||
![]() |
c65d0cefc1 | ||
![]() |
799e89af9e | ||
![]() |
381215fd73 | ||
![]() |
4fc3230fe6 | ||
![]() |
64bdaa46fb | ||
![]() |
6e16eaebba | ||
![]() |
a67f7f88e4 | ||
![]() |
ec8f467585 | ||
![]() |
9e3a66d5bf | ||
![]() |
c7409d1e04 | ||
![]() |
b64d01677b | ||
![]() |
9e8128ecb5 | ||
![]() |
131dcce0a1 | ||
![]() |
6dfdd8c1c2 | ||
![]() |
1b6c279850 | ||
![]() |
81c778a529 | ||
![]() |
f578a1cb2b | ||
![]() |
2576e66a55 | ||
![]() |
58e3b830e9 | ||
![]() |
1d35031024 | ||
![]() |
96b61755da | ||
![]() |
9f174c56ce | ||
![]() |
c8be9662bb | ||
![]() |
f53cd44c7a | ||
![]() |
9303764a83 | ||
![]() |
a5456a89dc | ||
![]() |
c974fc664c | ||
![]() |
eceead671e | ||
![]() |
4e7b554605 | ||
![]() |
a701da6386 | ||
![]() |
1f47fe47c7 | ||
![]() |
5dd07ac040 | ||
![]() |
ca8a2aeb7b | ||
![]() |
57fad1d4b2 | ||
![]() |
5c2720a931 | ||
![]() |
31e583e9f8 | ||
![]() |
0ac24e5a24 | ||
![]() |
98f53b6d3d | ||
![]() |
f5092cb73d | ||
![]() |
48d3bd1cca | ||
![]() |
5cffd4f673 | ||
![]() |
416f65fe01 | ||
![]() |
64c291ba78 | ||
![]() |
dcde2a0bae | ||
![]() |
f0ac0c19b7 | ||
![]() |
ba1b8533a5 | ||
![]() |
84e8927b1b | ||
![]() |
556300d59a | ||
![]() |
34f7b38f39 | ||
![]() |
f28d10d934 | ||
![]() |
d6adc59265 | ||
![]() |
c154dc00d7 | ||
![]() |
6d5dab38dd | ||
![]() |
480f4d4cf1 | ||
![]() |
f8271fec12 | ||
![]() |
4800f1d8f2 | ||
![]() |
48ce8e9fb7 | ||
![]() |
f746e5b7d7 | ||
![]() |
cdce257512 | ||
![]() |
227ab998bc | ||
![]() |
9384bff6f9 | ||
![]() |
9947d3e67f | ||
![]() |
67f01fbdb6 | ||
![]() |
164b5b0cf3 | ||
![]() |
56bb1dddd7 | ||
![]() |
e6b1cf540b | ||
![]() |
d61c83ace8 | ||
![]() |
4139024b3d | ||
![]() |
48fe8666c9 | ||
![]() |
90a44a0c07 | ||
![]() |
e013c19223 | ||
![]() |
4684d05fa6 | ||
![]() |
1c3a1c842d | ||
![]() |
f5127686fe | ||
![]() |
2b0275a1c8 | ||
![]() |
028693c380 | ||
![]() |
63920be17e | ||
![]() |
96888cce9c | ||
![]() |
bcc39be784 | ||
![]() |
b6314b4c4b | ||
![]() |
23d895415a | ||
![]() |
518ce0187a | ||
![]() |
1c69498c58 | ||
![]() |
615f6f50ce | ||
![]() |
79df6a646b | ||
![]() |
89d68fb2af | ||
![]() |
03b674ce73 | ||
![]() |
9c421997bf | ||
![]() |
3813433e02 | ||
![]() |
73509fc189 | ||
![]() |
7c9b7fa311 | ||
![]() |
cbba22c947 | ||
![]() |
f8529d4fe6 | ||
![]() |
2a206ef309 | ||
![]() |
6200c0dc46 | ||
![]() |
1760310123 | ||
![]() |
c8ed28e9c6 | ||
![]() |
56d4784b11 | ||
![]() |
4d3adaa557 | ||
![]() |
6830cf9dcf | ||
![]() |
4ba288501d | ||
![]() |
9dcd0604f2 | ||
![]() |
5a9b0c7142 | ||
![]() |
823d6c9c0f | ||
![]() |
57e26eb832 | ||
![]() |
f9c2c2b558 | ||
![]() |
a0e9dfbec2 | ||
![]() |
cb6f61cf37 | ||
![]() |
964e18ab49 | ||
![]() |
709acd8920 | ||
![]() |
6f0aecbfb3 | ||
![]() |
7ebe56fdd9 | ||
![]() |
fae235197f | ||
![]() |
141f518bdd | ||
![]() |
d9fefbbc5a | ||
![]() |
122d71a05d | ||
![]() |
3b4825dd29 | ||
![]() |
0c1ecc96a8 | ||
![]() |
39c9e92f42 | ||
![]() |
08810991c2 | ||
![]() |
f8581c4d6a | ||
![]() |
0d8498f1d1 | ||
![]() |
7198db758d | ||
![]() |
7a4743d00e | ||
![]() |
ae85c2a979 | ||
![]() |
de4bdfcd1f | ||
![]() |
9c7b930f8f | ||
![]() |
9e8bca4879 | ||
![]() |
5bd3934c00 | ||
![]() |
57212d5a35 | ||
![]() |
7236f83999 | ||
![]() |
c8ece786dd | ||
![]() |
60fae87e59 | ||
![]() |
3145b10f51 | ||
![]() |
75047a26f8 | ||
![]() |
1e4bf90c60 | ||
![]() |
b3a31b69ee | ||
![]() |
88475f2e2b | ||
![]() |
3cf0896998 | ||
![]() |
e0a53d4747 | ||
![]() |
669cbcd25a | ||
![]() |
2f7c19f139 | ||
![]() |
e131f22642 | ||
![]() |
a09e33bf8e | ||
![]() |
cd3c34e7b9 | ||
![]() |
b3733ef32a | ||
![]() |
f3b9674fed | ||
![]() |
b0cd456753 | ||
![]() |
c613d25f29 | ||
![]() |
fff9ceccc2 | ||
![]() |
4bcbeae1e0 | ||
![]() |
4c56e87e36 | ||
![]() |
011b96ff98 | ||
![]() |
d563f5fc87 | ||
![]() |
9c2df5ce19 | ||
![]() |
1745c485f3 | ||
![]() |
a53db82ae4 | ||
![]() |
a20a83eb76 | ||
![]() |
65e5a43e46 | ||
![]() |
b6afdf1201 | ||
![]() |
08a00ee21b | ||
![]() |
41a1e98cb9 | ||
![]() |
99da022775 | ||
![]() |
1ee25b4234 | ||
![]() |
c5e607a310 | ||
![]() |
5a0bad3b2f | ||
![]() |
6a99f20828 | ||
![]() |
91ca502e10 | ||
![]() |
1e5c37ee78 | ||
![]() |
ef2cdc0c6a | ||
![]() |
c94c08c6b4 | ||
![]() |
5de8edced6 | ||
![]() |
8906ce07de | ||
![]() |
9d66fc491c | ||
![]() |
f8a838db5d | ||
![]() |
e4ba736d03 | ||
![]() |
cc291e8c98 | ||
![]() |
c00d217a53 | ||
![]() |
9d853897cd | ||
![]() |
88c77f9c8a | ||
![]() |
1a7278f1d3 | ||
![]() |
9916d455f9 | ||
![]() |
541707f9a8 | ||
![]() |
00cf036d58 | ||
![]() |
9c68f24cfc | ||
![]() |
02c4512b00 | ||
![]() |
be84b189dc | ||
![]() |
313f2a1894 | ||
![]() |
a1ae1a1e95 | ||
![]() |
4874bcf8e8 | ||
![]() |
280ff9211f | ||
![]() |
494f658dee | ||
![]() |
c144d0a00d | ||
![]() |
696a36bd5a | ||
![]() |
3a3fc5e13a | ||
![]() |
0b89cc306e | ||
![]() |
dbc68047ad | ||
![]() |
2fdfe3a854 | ||
![]() |
d685d693e8 | ||
![]() |
b0cfdfa257 | ||
![]() |
7b6909f2c0 | ||
![]() |
45553c5f61 | ||
![]() |
1efb9d41db | ||
![]() |
9292b39cf5 | ||
![]() |
9eb26ab90c | ||
![]() |
f801f299c9 | ||
![]() |
7f18cae2e4 | ||
![]() |
a3794f8c3b | ||
![]() |
31a88e7ccc | ||
![]() |
4074e3a9e4 | ||
![]() |
c71e586c53 | ||
![]() |
1039fb5582 | ||
![]() |
7f60acdfdd | ||
![]() |
aef2c5dc14 | ||
![]() |
8d02986b0e | ||
![]() |
461da92064 | ||
![]() |
b20b773189 | ||
![]() |
74125c0922 | ||
![]() |
5023432802 | ||
![]() |
4a14248004 | ||
![]() |
1b241fc97a | ||
![]() |
09d1ab7abe | ||
![]() |
d28cb93835 | ||
![]() |
6b43338e06 | ||
![]() |
9a7a49350c | ||
![]() |
f85629afa6 | ||
![]() |
a39473a912 | ||
![]() |
05d0e9b8bb | ||
![]() |
b68c3b7f55 | ||
![]() |
0fbed6dec1 | ||
![]() |
16af2a2998 | ||
![]() |
7a40ac52a8 | ||
![]() |
6352d1b1ca | ||
![]() |
c93fc161dc | ||
![]() |
5b7de2bc2f | ||
![]() |
04ba7eb987 | ||
![]() |
5a2267bdb4 | ||
![]() |
0075c57bd4 | ||
![]() |
87873c9a73 | ||
![]() |
380e0abbe4 | ||
![]() |
e4cc89b2d9 | ||
![]() |
14dea04ee9 | ||
![]() |
6f6cbeba80 | ||
![]() |
974ed0166c | ||
![]() |
a91920a8ff | ||
![]() |
7cd38dde09 | ||
![]() |
b63a794fbe | ||
![]() |
3e862b85d4 | ||
![]() |
85bca660d1 | ||
![]() |
87704f49c9 | ||
![]() |
9f9cbb8823 | ||
![]() |
fa6ef6f1c7 | ||
![]() |
db354f5e53 | ||
![]() |
d3335f9947 | ||
![]() |
1a67062e1e | ||
![]() |
fec1a4ac32 | ||
![]() |
7f439b01a3 | ||
![]() |
0fd6f83766 | ||
![]() |
b15b2125e2 | ||
![]() |
3711bd0d24 | ||
![]() |
e086f09d48 | ||
![]() |
324bd95c91 | ||
![]() |
5d122c3bc8 | ||
![]() |
8e123e7fc9 | ||
![]() |
e8163124e3 | ||
![]() |
0651be8366 | ||
![]() |
4957035be6 | ||
![]() |
bcd55c0b75 | ||
![]() |
914ee92015 | ||
![]() |
fb9194b537 | ||
![]() |
c04490bd52 | ||
![]() |
b601f4dc15 | ||
![]() |
4086190c80 | ||
![]() |
e1eea9d98a | ||
![]() |
393d57b387 | ||
![]() |
b283fe07af | ||
![]() |
feeb21577f | ||
![]() |
1fca16737d | ||
![]() |
4ec85a12e3 | ||
![]() |
3d2aebccad | ||
![]() |
22382f799c | ||
![]() |
468eceabff | ||
![]() |
08e0eb79f5 | ||
![]() |
8b03ce562c | ||
![]() |
997311ba14 | ||
![]() |
cbd031ca7f | ||
![]() |
95842e7984 | ||
![]() |
cc41e95806 | ||
![]() |
2c77e088b4 | ||
![]() |
4eefc2e47c | ||
![]() |
7c13666226 | ||
![]() |
4ed8313954 | ||
![]() |
c8e2ab6781 | ||
![]() |
b2ed29b8c0 | ||
![]() |
1789b56a85 | ||
![]() |
714dbc9294 | ||
![]() |
f40eb963fd | ||
![]() |
ee4b49d12f | ||
![]() |
4c9942534c | ||
![]() |
d5f7db59a8 | ||
![]() |
8d1b73ae89 | ||
![]() |
afa77099cf | ||
![]() |
f68fab53a7 | ||
![]() |
6a4dc281d1 | ||
![]() |
26831a417a | ||
![]() |
6293818c35 | ||
![]() |
a859de6806 | ||
![]() |
13202bfce4 | ||
![]() |
f3d031b38a | ||
![]() |
f6dbf38e99 | ||
![]() |
d0a4270449 | ||
![]() |
b9a7a9981e | ||
![]() |
d9fb152b95 | ||
![]() |
09a0f7a45c | ||
![]() |
9396ffb509 | ||
![]() |
66a8afd18c | ||
![]() |
e7e07c39c5 | ||
![]() |
201e081750 | ||
![]() |
5f7f1d738d | ||
![]() |
8a3d144714 | ||
![]() |
44080154b0 | ||
![]() |
c17493f2d3 | ||
![]() |
dcfc1ca6a0 | ||
![]() |
07e1bae0af | ||
![]() |
658a7f1ca7 | ||
![]() |
bd408cd627 | ||
![]() |
7c520f6475 | ||
![]() |
07ee11b963 | ||
![]() |
a4c7041561 | ||
![]() |
d7eb223343 | ||
![]() |
ddc048e2c3 | ||
![]() |
625ab6decd | ||
![]() |
2638396df5 | ||
![]() |
3d8e285e2b | ||
![]() |
793a12d58d | ||
![]() |
23b4688c44 | ||
![]() |
c4d8888972 | ||
![]() |
6e50031c49 | ||
![]() |
833ca1e350 | ||
![]() |
a888b3a707 | ||
![]() |
d9bbe39f48 | ||
![]() |
38226dfe81 | ||
![]() |
94ef1e13e0 | ||
![]() |
35c7c005ba | ||
![]() |
1e732f9b9b | ||
![]() |
ee5c471280 | ||
![]() |
8ef47147a0 | ||
![]() |
fb4dd4e1ce | ||
![]() |
a19f3a4f9f | ||
![]() |
c3ba52a1fc | ||
![]() |
1b2bd0cc0a | ||
![]() |
dd3779d64b | ||
![]() |
806d1483a8 | ||
![]() |
b1f28d6003 | ||
![]() |
50e6950fa8 | ||
![]() |
1d60d3cd2b | ||
![]() |
59ed18e112 | ||
![]() |
c4c1044427 | ||
![]() |
791b3c65f3 | ||
![]() |
a3765cd363 | ||
![]() |
3c5b864396 | ||
![]() |
b09654b53a | ||
![]() |
58fc857a2d | ||
![]() |
e443ee357a | ||
![]() |
2618294eb7 | ||
![]() |
03352cb4ad | ||
![]() |
2c851498cc | ||
![]() |
a52292ae17 | ||
![]() |
f6a886f092 | ||
![]() |
6234ae5a8b | ||
![]() |
a4ead670ed | ||
![]() |
303d981099 | ||
![]() |
7727311df7 | ||
![]() |
5f54a8e94e | ||
![]() |
b8bfc98618 | ||
![]() |
6e6f72a521 | ||
![]() |
a654c5d643 | ||
![]() |
c5d6aa169f | ||
![]() |
c1c67286d3 | ||
![]() |
2fb34697c7 | ||
![]() |
94b5b9f370 | ||
![]() |
a9467513e1 | ||
![]() |
17d944f6ce | ||
![]() |
0f82f18652 | ||
![]() |
3db3e577f1 | ||
![]() |
37ee821947 | ||
![]() |
916ab9a7e6 | ||
![]() |
1802cf9fd1 | ||
![]() |
1bf7d30623 | ||
![]() |
b2d89253a6 | ||
![]() |
7727630f24 | ||
![]() |
e2f8c9a4c2 | ||
![]() |
22dbfec126 | ||
![]() |
efc462eebf | ||
![]() |
c3883e2fe0 | ||
![]() |
16c91a3f4b | ||
![]() |
80bb5a0542 | ||
![]() |
906d58a918 | ||
![]() |
23b34fe142 | ||
![]() |
8d6f503e04 | ||
![]() |
3284a61f43 | ||
![]() |
c1a852d0e8 | ||
![]() |
4c90ad3704 | ||
![]() |
a92a801f08 | ||
![]() |
9059c6c657 | ||
![]() |
bd6f8cd585 | ||
![]() |
a6024f476a | ||
![]() |
dfef30a737 | ||
![]() |
55ca3709ef | ||
![]() |
ab6ae7520d | ||
![]() |
c7a300d12d | ||
![]() |
babf322695 | ||
![]() |
642de1510b | ||
![]() |
f61eed1a82 | ||
![]() |
e9c40dead8 | ||
![]() |
31bf6d29b9 | ||
![]() |
571b6f0cdb | ||
![]() |
e7b9e8de14 | ||
![]() |
321553d5f9 | ||
![]() |
4d23068c5c | ||
![]() |
634b38169a | ||
![]() |
fe4695f92a | ||
![]() |
3c7941b24b | ||
![]() |
a5d7f5e1fa | ||
![]() |
0dfd7e3d8c | ||
![]() |
f757dfdc4f | ||
![]() |
eb675ad1b1 | ||
![]() |
08a5768764 | ||
![]() |
d888bb1902 | ||
![]() |
6dc708f53f | ||
![]() |
60033f3eb4 | ||
![]() |
1239e075b8 | ||
![]() |
85286d74ef | ||
![]() |
f8f646497c | ||
![]() |
24ace6da25 | ||
![]() |
ea23ea28ce | ||
![]() |
4072a6e445 | ||
![]() |
54601b292a | ||
![]() |
964e193fcd | ||
![]() |
d5909b6d5d | ||
![]() |
67a00ee541 | ||
![]() |
d0eea87b1a | ||
![]() |
273fc329e0 | ||
![]() |
d85be95c30 | ||
![]() |
ff60cf8a4c | ||
![]() |
b9daeef524 | ||
![]() |
9383ceac30 | ||
![]() |
3d2bd08132 | ||
![]() |
1259fdc2e3 | ||
![]() |
a564b7eedb | ||
![]() |
ebf1fc7ca4 | ||
![]() |
73708e7be7 | ||
![]() |
0c409aa290 | ||
![]() |
edfa329e64 | ||
![]() |
5ab2c31500 | ||
![]() |
50c1e3738a | ||
![]() |
7a939746ae | ||
![]() |
ce178c7e1f | ||
![]() |
1615fbc30e | ||
![]() |
45a4fcab1e | ||
![]() |
68b19ae087 | ||
![]() |
a073db1e52 | ||
![]() |
10940da381 | ||
![]() |
57d9cffc42 | ||
![]() |
65a3062f45 | ||
![]() |
ac25f3456a | ||
![]() |
f4f79a3d5f | ||
![]() |
7bf43a9712 | ||
![]() |
97da29cc90 | ||
![]() |
de1da485ab | ||
![]() |
2b10cf3c15 | ||
![]() |
432675d4c2 | ||
![]() |
70ac638d93 | ||
![]() |
ccbacb369b | ||
![]() |
7c0b561a16 | ||
![]() |
db5f270f42 | ||
![]() |
3a86956257 | ||
![]() |
feac1a3f56 | ||
![]() |
f49d4ef4ad | ||
![]() |
2859845567 | ||
![]() |
f3c37e484e | ||
![]() |
e521b58b01 | ||
![]() |
34182990ef | ||
![]() |
1ca5d6baa6 | ||
![]() |
9365f68454 | ||
![]() |
7b5f107341 | ||
![]() |
e9ec03f769 | ||
![]() |
579a6ee299 | ||
![]() |
7d31659137 | ||
![]() |
f549c611ab | ||
![]() |
51c0a03e94 | ||
![]() |
19c73680cf | ||
![]() |
a9ba9a5f92 | ||
![]() |
3032792563 | ||
![]() |
7ccc4ddf0d | ||
![]() |
b9704715fb | ||
![]() |
86e7dff2fc | ||
![]() |
c450f644e6 | ||
![]() |
496184b1bc | ||
![]() |
b81cac3305 | ||
![]() |
bf9dd24536 | ||
![]() |
e5680c712b | ||
![]() |
44daa6d3e6 | ||
![]() |
5ddddbb16f | ||
![]() |
5aaa3f50aa | ||
![]() |
7d26cd74c1 | ||
![]() |
7e6941acc8 | ||
![]() |
63d865666e | ||
![]() |
5dda4084df | ||
![]() |
19f9238ef0 | ||
![]() |
491cc8f54d | ||
![]() |
cad35a83fb | ||
![]() |
5fbe5951ab | ||
![]() |
21590e60e6 | ||
![]() |
fe69ad0861 | ||
![]() |
9daf8f6de5 | ||
![]() |
8ca6606323 | ||
![]() |
09a2da89a2 | ||
![]() |
9a0b3775d8 | ||
![]() |
2075a9a999 | ||
![]() |
1090cc964a | ||
![]() |
39476bed85 | ||
![]() |
49130c2018 | ||
![]() |
94af199c49 | ||
![]() |
2d25f6f57f | ||
![]() |
cf179ec294 | ||
![]() |
4d6f220a2f | ||
![]() |
0ffbe5b5ea | ||
![]() |
5b83c834ac | ||
![]() |
da7f32bddb | ||
![]() |
9a5eac4ea9 | ||
![]() |
6571b5d118 | ||
![]() |
12dff8e382 | ||
![]() |
c4da87a0cb | ||
![]() |
446f8f29d3 | ||
![]() |
48cc76f114 | ||
![]() |
a0892b852e | ||
![]() |
485c7805eb | ||
![]() |
23802f4489 | ||
![]() |
3fedd978a2 | ||
![]() |
a9f1bed922 | ||
![]() |
eb23788fec | ||
![]() |
f6d73555a6 | ||
![]() |
a56a709406 | ||
![]() |
5f253e66f6 | ||
![]() |
4669f7e2b9 | ||
![]() |
4c90f88704 | ||
![]() |
a7213b78d6 | ||
![]() |
719333e16e | ||
![]() |
100e471b49 | ||
![]() |
3f2016e552 | ||
![]() |
dd89ea4505 | ||
![]() |
5cea03bb4f | ||
![]() |
101e12cf9a | ||
![]() |
f382808450 | ||
![]() |
0cbe3c2a93 | ||
![]() |
4f0ae28359 | ||
![]() |
6a4250f485 | ||
![]() |
3322b29e6a | ||
![]() |
26467e61a3 | ||
![]() |
33ac472601 | ||
![]() |
2bb4c7cfa7 | ||
![]() |
fbdd2324a5 | ||
![]() |
dc31aa6a61 | ||
![]() |
5b28a987e5 | ||
![]() |
78d28063c4 | ||
![]() |
64647edbe1 | ||
![]() |
b36f5f1ec4 | ||
![]() |
592649d3f2 | ||
![]() |
9398599816 | ||
![]() |
61c29473d3 | ||
![]() |
5fd2fc77f3 | ||
![]() |
3eb0681e3d | ||
![]() |
e831af35fd | ||
![]() |
fb2e0f734e | ||
![]() |
3d69f43cff | ||
![]() |
41b1931d91 | ||
![]() |
f086444e4a | ||
![]() |
b8210bbc45 | ||
![]() |
ce2965b5bc | ||
![]() |
18000ff499 | ||
![]() |
64e91d6eb7 | ||
![]() |
6f7dce6dca | ||
![]() |
175524ad02 | ||
![]() |
bfbde72676 | ||
![]() |
f76583a08a | ||
![]() |
6d39b401b5 | ||
![]() |
f448bfd3f3 | ||
![]() |
64b0587e78 | ||
![]() |
9fe813e572 | ||
![]() |
b0da0bbf8b | ||
![]() |
5c44082b77 | ||
![]() |
df7ed27b78 | ||
![]() |
bef7ce37d9 | ||
![]() |
c74cf79e0f | ||
![]() |
992488c6c6 | ||
![]() |
bdfe5e97cd | ||
![]() |
df069bc456 | ||
![]() |
9bb67ae7f0 | ||
![]() |
c994fe4879 | ||
![]() |
32421fed89 | ||
![]() |
077add79cd | ||
![]() |
50f0163f5f | ||
![]() |
481c1b6f2e | ||
![]() |
474b6610fa | ||
![]() |
11c49da871 | ||
![]() |
678828b56f | ||
![]() |
9e802ad721 | ||
![]() |
859ffddea3 | ||
![]() |
daf935d310 | ||
![]() |
6953800628 | ||
![]() |
7c65c4f485 | ||
![]() |
8950fb9393 | ||
![]() |
b754f3dc34 | ||
![]() |
e8a25dc8d9 | ||
![]() |
99765cc704 | ||
![]() |
a868dbce3b | ||
![]() |
f68305bf00 | ||
![]() |
517c234461 | ||
![]() |
a58275591d | ||
![]() |
a2f4fb9ddb | ||
![]() |
b2acf02af9 | ||
![]() |
d5b1ca1a52 | ||
![]() |
339b9f6e7b | ||
![]() |
35cfa6efa3 | ||
![]() |
34ec024bf4 | ||
![]() |
75aa08ed6b | ||
![]() |
c8dc1e0665 | ||
![]() |
847ae7dd7c | ||
![]() |
44f55e1866 | ||
![]() |
c391adad10 | ||
![]() |
f578b06d83 | ||
![]() |
0748f75d9b | ||
![]() |
7dff29bcd1 | ||
![]() |
3e09f95b40 | ||
![]() |
7065425927 | ||
![]() |
f01793ad4a | ||
![]() |
1f495efb46 | ||
![]() |
38d8359384 | ||
![]() |
2c01e79b47 | ||
![]() |
a573d57de8 | ||
![]() |
2f4e2935a3 | ||
![]() |
44beae519d | ||
![]() |
b896711fc6 | ||
![]() |
76bdfabcc5 | ||
![]() |
b35e8a588f | ||
![]() |
6c48f5ac63 | ||
![]() |
1448f52eac | ||
![]() |
c96e8ab47c | ||
![]() |
2c4ef4460f | ||
![]() |
20bbe1b57b | ||
![]() |
449d59af2f | ||
![]() |
851136e6fd | ||
![]() |
ad854e9867 | ||
![]() |
f53dde970f | ||
![]() |
046a385070 | ||
![]() |
9a28176d72 | ||
![]() |
ba2df05fb4 | ||
![]() |
8722c51306 | ||
![]() |
80104eb6d5 | ||
![]() |
8743326924 | ||
![]() |
83a6cb804b | ||
![]() |
78801f303e | ||
![]() |
9691b609a2 | ||
![]() |
250fa8c732 | ||
![]() |
2cd5f4cd3e | ||
![]() |
dcd7c6337c | ||
![]() |
43d633f560 | ||
![]() |
1c37b0487c | ||
![]() |
acc92b156b | ||
![]() |
aeadae5399 | ||
![]() |
42d5b05f54 | ||
![]() |
e133f621a4 | ||
![]() |
91437d9e63 | ||
![]() |
f1b497fb0c | ||
![]() |
cc9d061e51 | ||
![]() |
62ee9a4d60 | ||
![]() |
082f72dd20 | ||
![]() |
9c19368fc7 | ||
![]() |
561d6fd478 | ||
![]() |
42a01822bf | ||
![]() |
38f1237d49 | ||
![]() |
8df77122e5 | ||
![]() |
fef6b9df80 | ||
![]() |
d52eac66db | ||
![]() |
70879f0abc | ||
![]() |
bcb393628e | ||
![]() |
18d3a5c12b | ||
![]() |
6ee3d0102b | ||
![]() |
fc9626e2f4 | ||
![]() |
3bedd94fc8 | ||
![]() |
45f81cde6d | ||
![]() |
a482c798ad | ||
![]() |
838af929a0 | ||
![]() |
d9b1f6223e | ||
![]() |
26577d1301 | ||
![]() |
6496c1b806 | ||
![]() |
7bb251dad8 | ||
![]() |
9027e5c5bb | ||
![]() |
8842650c33 | ||
![]() |
d5bf128cee | ||
![]() |
5cd86e272f | ||
![]() |
740cbe9e02 | ||
![]() |
ed890a273a | ||
![]() |
068cd559e1 | ||
![]() |
dc127f39a7 | ||
![]() |
7a99a7008c | ||
![]() |
70b451db7b | ||
![]() |
2ab03a0914 | ||
![]() |
2fa8c7d2db | ||
![]() |
7c759ba8b0 | ||
![]() |
6d9b452fde | ||
![]() |
8ba68fdb47 | ||
![]() |
e72d27566c | ||
![]() |
01c02a1ef8 | ||
![]() |
4bb4611e78 | ||
![]() |
f7eb1c9a83 | ||
![]() |
2d22e6dee4 | ||
![]() |
4587bf759d | ||
![]() |
e1e37cfe3c | ||
![]() |
381934985a | ||
![]() |
a8042885ac | ||
![]() |
a71e68db50 | ||
![]() |
1417578b3d | ||
![]() |
96befa138c | ||
![]() |
16a99804de | ||
![]() |
75a39ed279 | ||
![]() |
4d357ab77c | ||
![]() |
d4f3dd49b4 | ||
![]() |
4ec6d0555a | ||
![]() |
a6a1182c4c | ||
![]() |
a59c9c602b | ||
![]() |
0c4d824d64 | ||
![]() |
a5281856c9 | ||
![]() |
0206a46d39 | ||
![]() |
9475ef2202 | ||
![]() |
edae00e719 | ||
![]() |
fb695bc55f | ||
![]() |
23a5b8fd3c | ||
![]() |
273a93cfcf | ||
![]() |
d105985d78 | ||
![]() |
f8cfeb39e9 | ||
![]() |
d5d3982d3c | ||
![]() |
47341107ea | ||
![]() |
90eaa87a4d | ||
![]() |
b09a54b2c2 | ||
![]() |
10aec174d5 | ||
![]() |
d32ed194e8 | ||
![]() |
70d0fbd715 | ||
![]() |
302432e157 | ||
![]() |
4ab8a677dc | ||
![]() |
52e4a4c904 | ||
![]() |
a0f6932ebe | ||
![]() |
6e700dab69 | ||
![]() |
35eaed7206 | ||
![]() |
e7c963f2ce | ||
![]() |
949d72e368 | ||
![]() |
8d2a184658 | ||
![]() |
c877a32d97 | ||
![]() |
541468f0ca | ||
![]() |
d2797effa3 | ||
![]() |
1170fb1e1e | ||
![]() |
65b9b3195c | ||
![]() |
258830e913 | ||
![]() |
c69b9780df | ||
![]() |
f869593ac8 | ||
![]() |
181b96dd2d | ||
![]() |
4e9f463bb6 | ||
![]() |
4947bb113d | ||
![]() |
4345a656aa | ||
![]() |
31dc8b73b7 | ||
![]() |
e84db4f411 | ||
![]() |
395bf34a91 | ||
![]() |
dcc7628b67 | ||
![]() |
f2a4ae15aa | ||
![]() |
434bcb08ee | ||
![]() |
99885c4cbc | ||
![]() |
ab196f7afc | ||
![]() |
90c3fe22f5 | ||
![]() |
ad8117e7c8 | ||
![]() |
82568c0b52 | ||
![]() |
838b19c2b5 | ||
![]() |
0d2f67b523 | ||
![]() |
a57bcd0238 | ||
![]() |
76497bd5f7 | ||
![]() |
53ec02d5e9 | ||
![]() |
7a5f485cf8 | ||
![]() |
90565b1e36 | ||
![]() |
6f7c7a2149 | ||
![]() |
91123983ad | ||
![]() |
2506edbffb | ||
![]() |
929520ecd2 | ||
![]() |
a20d467bf1 | ||
![]() |
102dc123e8 | ||
![]() |
b4d0b23369 | ||
![]() |
119a949bb5 | ||
![]() |
f5b22bf2c3 | ||
![]() |
5b7a89d714 | ||
![]() |
5b41857a22 | ||
![]() |
688d9ff114 | ||
![]() |
c2d202e9b0 | ||
![]() |
bc51bc2933 | ||
![]() |
f547a56b1d | ||
![]() |
1f6637d49a | ||
![]() |
de155a740c | ||
![]() |
f323fc48d7 | ||
![]() |
2ea634c302 | ||
![]() |
c0d38d941a | ||
![]() |
d7f2d90fd3 | ||
![]() |
8ea9b89321 | ||
![]() |
738254b2fc | ||
![]() |
fcc9a98cad | ||
![]() |
dcf0a3e475 | ||
![]() |
e990d6eecc | ||
![]() |
941636b87b | ||
![]() |
1298a82f4f | ||
![]() |
e6fedcbd10 | ||
![]() |
48c8c7daf5 | ||
![]() |
6e5d9d4490 | ||
![]() |
d2f2dde2e1 | ||
![]() |
d0dd035075 | ||
![]() |
7702643e1b | ||
![]() |
984c9c317a | ||
![]() |
2d7068a09d | ||
![]() |
d712c3b408 | ||
![]() |
0a623122dc | ||
![]() |
68c36c0d3f | ||
![]() |
f3ebaf8bdd | ||
![]() |
c472046cbb | ||
![]() |
02296adbba | ||
![]() |
a375f0f160 | ||
![]() |
fb961e89da | ||
![]() |
3e2bff296f | ||
![]() |
cf01db4d08 | ||
![]() |
08dbb4a69a | ||
![]() |
b5eff3cecd | ||
![]() |
89d66b6100 | ||
![]() |
c53ee41855 | ||
![]() |
3c798b4907 | ||
![]() |
78a9c17bc6 | ||
![]() |
8b77da545d | ||
![]() |
27d3dcf14c | ||
![]() |
5388759bd5 | ||
![]() |
e927655774 | ||
![]() |
3be8375e4f | ||
![]() |
27ba8c181f | ||
![]() |
1410bcbce9 | ||
![]() |
43b26ee612 | ||
![]() |
2c47357991 | ||
![]() |
7e2538e07a | ||
![]() |
851fcf4823 | ||
![]() |
63b9fb3d2b | ||
![]() |
a79454f6a6 | ||
![]() |
b5fbfe9aa6 | ||
![]() |
598894211f | ||
![]() |
dbfd0a833d | ||
![]() |
d45994268f | ||
![]() |
815b248990 | ||
![]() |
333b36f090 | ||
![]() |
b18ef61361 | ||
![]() |
86ae45108c | ||
![]() |
5ef29c424d | ||
![]() |
c8dfb57408 | ||
![]() |
b773ef3f0f | ||
![]() |
c8f54b3509 | ||
![]() |
00d5e6e9c7 | ||
![]() |
1132a1c9a7 | ||
![]() |
149d1a2f47 | ||
![]() |
17f2c0ae88 | ||
![]() |
ce0b4d5c19 | ||
![]() |
0c102a8f2f | ||
![]() |
415de497d3 | ||
![]() |
02d108774c | ||
![]() |
2c66d90626 | ||
![]() |
e4cdf29951 | ||
![]() |
b5d224ce22 | ||
![]() |
c937a299f7 | ||
![]() |
6d4ec8b46f | ||
![]() |
941f2ca60d | ||
![]() |
7a1b996da9 | ||
![]() |
302a571367 | ||
![]() |
ec195acf4f | ||
![]() |
8f41e05a39 | ||
![]() |
35b6d65f27 | ||
![]() |
e602bcf41e | ||
![]() |
8c5ba50840 | ||
![]() |
ff239fff4f | ||
![]() |
abdbaf605e | ||
![]() |
47b3577d6b | ||
![]() |
2ffc7c2088 | ||
![]() |
1f56960c44 | ||
![]() |
dfc5b4972b | ||
![]() |
18c3c2118d | ||
![]() |
42f6a0441c | ||
![]() |
3b9aab0684 | ||
![]() |
7a46c57ef3 | ||
![]() |
118b76a8f1 | ||
![]() |
a302d34f6d | ||
![]() |
148aca23be | ||
![]() |
8ab2722300 | ||
![]() |
bd25528ca3 | ||
![]() |
9f02d8978b | ||
![]() |
7e51d1dba0 | ||
![]() |
4d4755352d | ||
![]() |
0360f79e6f | ||
![]() |
6d8f92c185 | ||
![]() |
c989327eb0 | ||
![]() |
22eccaa64f | ||
![]() |
740cc8ce49 | ||
![]() |
70eafba0d4 | ||
![]() |
79fb9834d9 | ||
![]() |
146db77025 | ||
![]() |
e6d607fef3 | ||
![]() |
ca46992d3e | ||
![]() |
c83ab0dc58 | ||
![]() |
d9952b9054 | ||
![]() |
04e60d67ae | ||
![]() |
f4adbdbe31 | ||
![]() |
484b820dfc | ||
![]() |
9bcba41cd6 | ||
![]() |
eabc487e59 | ||
![]() |
5c5e522fcd | ||
![]() |
f687028c87 | ||
![]() |
05f0b9c29c | ||
![]() |
4a8ec20858 | ||
![]() |
c5e36a7dd8 | ||
![]() |
d5fafa1340 | ||
![]() |
e8994b43a0 | ||
![]() |
6ada2c9c3f | ||
![]() |
bbf190fc42 | ||
![]() |
963ff1997d | ||
![]() |
5c2a576546 | ||
![]() |
452400b7ef | ||
![]() |
20a5276560 | ||
![]() |
c94eee6199 | ||
![]() |
2a75222fe2 | ||
![]() |
7d9d173c9f | ||
![]() |
f248fe2dec | ||
![]() |
e0df0b6d74 | ||
![]() |
a41414e0e0 | ||
![]() |
f1cea1f152 | ||
![]() |
02f5698868 | ||
![]() |
e25816fc05 | ||
![]() |
5b07c8fc6c | ||
![]() |
b45afd1cab | ||
![]() |
8b73257a86 | ||
![]() |
def6fffcf1 | ||
![]() |
78b13577ef | ||
![]() |
1f5d50ccce | ||
![]() |
ebf65572dd | ||
![]() |
c30c293d6f | ||
![]() |
9ee1be6fba | ||
![]() |
76177dbc79 | ||
![]() |
6cc532c9a0 | ||
![]() |
3cc770a00b | ||
![]() |
7ef0bfbdf1 | ||
![]() |
bafde1900b | ||
![]() |
5844242cfb | ||
![]() |
7e7cdf73b9 | ||
![]() |
5ca74acd8d | ||
![]() |
cd2aaac0c2 | ||
![]() |
ff8a7225fc | ||
![]() |
c6eac285ea | ||
![]() |
d0be122fc6 | ||
![]() |
01e5a7b1b5 | ||
![]() |
0b42018377 | ||
![]() |
7cb6c70bc2 | ||
![]() |
f31ae7c556 | ||
![]() |
5728c514d1 | ||
![]() |
debac0dfd4 | ||
![]() |
80b1f49af7 | ||
![]() |
868958e159 | ||
![]() |
52af03a5a6 | ||
![]() |
7475e971bc | ||
![]() |
c65b1fee8d | ||
![]() |
e2abade6aa | ||
![]() |
57673136d8 | ||
![]() |
49e122a81d | ||
![]() |
74976d3199 | ||
![]() |
a750e4afa7 | ||
![]() |
c122e990a3 | ||
![]() |
b1422fbda4 | ||
![]() |
d91da96798 | ||
![]() |
b3897df682 | ||
![]() |
3cacb56bb7 | ||
![]() |
15a1973e28 | ||
![]() |
ad7d47a8ba | ||
![]() |
0948c607b6 | ||
![]() |
60d04052c5 | ||
![]() |
c1780ac657 | ||
![]() |
e49cf0ec38 | ||
![]() |
b96e44b362 | ||
![]() |
8a7b7dffec | ||
![]() |
7774e78864 | ||
![]() |
e1d641f684 | ||
![]() |
4efd0a9f77 | ||
![]() |
f6f8751332 | ||
![]() |
abb28593ce | ||
![]() |
115693b046 | ||
![]() |
d7de8b1453 | ||
![]() |
3242943cd1 | ||
![]() |
6adff85fd6 | ||
![]() |
8b1ff3f005 | ||
![]() |
756621c6e4 | ||
![]() |
7e2ab1fd53 | ||
![]() |
c7a0597fe7 | ||
![]() |
253d551684 | ||
![]() |
00b7535cf1 | ||
![]() |
5945877313 | ||
![]() |
fe3e1d31bf | ||
![]() |
65f1cafa43 | ||
![]() |
d9b1f2fefa | ||
![]() |
e3ef0929f1 | ||
![]() |
8860962e09 | ||
![]() |
f13b8f669d | ||
![]() |
e4b055eb6d | ||
![]() |
fa58db798b | ||
![]() |
45b13fc2a6 | ||
![]() |
cf3f3a7750 | ||
![]() |
e455b93fac | ||
![]() |
cf762f2a9d | ||
![]() |
8ad0d919b1 | ||
![]() |
4b5c8d1f3e | ||
![]() |
eb6c649d47 | ||
![]() |
f2b025ca6c | ||
![]() |
4328a6f77d | ||
![]() |
97a6851525 | ||
![]() |
846d7dc317 | ||
![]() |
f62d61f088 | ||
![]() |
1f33ac3e98 | ||
![]() |
ed08a4bd58 | ||
![]() |
40d0828ccd | ||
![]() |
66029c405f | ||
![]() |
7d8c076e06 | ||
![]() |
05dcac55f9 | ||
![]() |
2aa42d2647 | ||
![]() |
dc8e0c9c5c | ||
![]() |
96ae659fdf | ||
![]() |
124e75c286 | ||
![]() |
25da798e7a | ||
![]() |
88d5347d94 | ||
![]() |
7f3868727b | ||
![]() |
b09a776a26 | ||
![]() |
3103e8d719 | ||
![]() |
cfbd751742 | ||
![]() |
a38eabc8bc | ||
![]() |
9669ea6a12 | ||
![]() |
863a28daa3 | ||
![]() |
ea5bcfed8b | ||
![]() |
f6f3c4c25b | ||
![]() |
8fe29f5728 | ||
![]() |
9866adff95 | ||
![]() |
a8b0c55818 | ||
![]() |
cac88e8be5 | ||
![]() |
e9f6a3482c | ||
![]() |
5d2e80f188 | ||
![]() |
cfd4d5b13e | ||
![]() |
06514aec63 | ||
![]() |
4ded1ae67b | ||
![]() |
1da974e3fa | ||
![]() |
94f06f0946 | ||
![]() |
d9eec8a455 | ||
![]() |
eaecbcafb2 | ||
![]() |
73b5d0a9b9 | ||
![]() |
1adc2c0c51 | ||
![]() |
a3b32819b1 | ||
![]() |
a57cf443ed | ||
![]() |
d0d095cbcd | ||
![]() |
3239fce5c1 | ||
![]() |
200b770104 | ||
![]() |
3aa959eda7 | ||
![]() |
72c047f4f0 | ||
![]() |
c71958af4b | ||
![]() |
ce13d82657 | ||
![]() |
73dc8ff6bd | ||
![]() |
3278ad2ed8 | ||
![]() |
2e453ef58a | ||
![]() |
a5d04be21f | ||
![]() |
9467df526c | ||
![]() |
9ca75589c0 | ||
![]() |
c158abe87c | ||
![]() |
53530bd1d5 | ||
![]() |
d7f545721b | ||
![]() |
8b72cb64b2 | ||
![]() |
852df2239e | ||
![]() |
5a1fbe60af | ||
![]() |
88e33b7b32 | ||
![]() |
264b3f8543 | ||
![]() |
ac2bce8b7b | ||
![]() |
9f3faaf3c4 | ||
![]() |
7be39e07e1 | ||
![]() |
154fb4317f | ||
![]() |
aad5210820 | ||
![]() |
7c4fca0ae1 | ||
![]() |
1a6ec716ef | ||
![]() |
0b22cc962d | ||
![]() |
51769c40d8 | ||
![]() |
85ab89a08b | ||
![]() |
a43062c575 | ||
![]() |
c943e27d51 | ||
![]() |
fb5d77158a | ||
![]() |
3023816491 | ||
![]() |
51e17e89f4 | ||
![]() |
98c24d1a32 | ||
![]() |
c2d0f35e7a | ||
![]() |
ab99a57997 | ||
![]() |
c8ebaf3521 | ||
![]() |
f1dc1af85d | ||
![]() |
7b3d870516 | ||
![]() |
963ff846ff | ||
![]() |
48c4081ba1 | ||
![]() |
970ccf043b | ||
![]() |
0c92c12adf | ||
![]() |
52d00f7e30 | ||
![]() |
309491a6d8 | ||
![]() |
29143dec87 | ||
![]() |
0fd4c14043 | ||
![]() |
81e1f87e8c | ||
![]() |
a448d04d46 | ||
![]() |
60e640af5a | ||
![]() |
df15c418c7 | ||
![]() |
04ab8ae27b | ||
![]() |
b4a64af15e | ||
![]() |
1ccfd1fb67 | ||
![]() |
838c057231 | ||
![]() |
af544bb16f | ||
![]() |
84fd401d21 | ||
![]() |
b78d6c9dd7 | ||
![]() |
b5d224496f | ||
![]() |
78d8b5f73c | ||
![]() |
5670d98c54 | ||
![]() |
440c676be2 | ||
![]() |
e820222c5b | ||
![]() |
1786eef8e6 | ||
![]() |
5f2fa1dbe3 | ||
![]() |
77d257de27 | ||
![]() |
89d950e9a7 | ||
![]() |
4041d87c34 | ||
![]() |
b3c5e6a3cc | ||
![]() |
1da8faa285 | ||
![]() |
988f5d1b5d | ||
![]() |
3ab3d5555e | ||
![]() |
2e5d46c399 | ||
![]() |
7600ad7cac | ||
![]() |
485ad583a0 | ||
![]() |
bbc82a9892 | ||
![]() |
69596106d3 | ||
![]() |
6913148d99 | ||
![]() |
14460c6b6d | ||
![]() |
a0a11be79b | ||
![]() |
4fa5bd9d5c | ||
![]() |
e7bfd32ccc | ||
![]() |
6f283b52ab | ||
![]() |
714bb991aa | ||
![]() |
32bddfabea | ||
![]() |
1944c826bc | ||
![]() |
619bb60b26 | ||
![]() |
c549e16ed1 | ||
![]() |
01c9c4507f | ||
![]() |
a1359f2388 | ||
![]() |
ab687481cc | ||
![]() |
0efbd4df8b | ||
![]() |
caaa050e60 | ||
![]() |
8c9d7bf07e | ||
![]() |
44ef34db88 | ||
![]() |
5781f223f6 | ||
![]() |
b4e3891912 | ||
![]() |
160f793e2a | ||
![]() |
1429d6bfb9 | ||
![]() |
ced6a5ae07 | ||
![]() |
d6d0f78e93 | ||
![]() |
e4c8ebe056 | ||
![]() |
76b25a1377 | ||
![]() |
ccc3ee663b | ||
![]() |
0626661764 | ||
![]() |
31db04a3ca | ||
![]() |
0c7163b9db | ||
![]() |
7d78cad8af | ||
![]() |
912530ed20 | ||
![]() |
d3f37199b9 | ||
![]() |
a4748d84b0 | ||
![]() |
8f847ec381 | ||
![]() |
3a70f09dd3 | ||
![]() |
568f63100b | ||
![]() |
2937a55582 | ||
![]() |
209e4e940e | ||
![]() |
cd093a6014 | ||
![]() |
3e25916b37 | ||
![]() |
5f9438dae6 | ||
![]() |
99e65c58ce | ||
![]() |
df71b07e9d | ||
![]() |
2694195215 | ||
![]() |
b0d6c0e7cb | ||
![]() |
66450d1f3c | ||
![]() |
76efea3aa7 | ||
![]() |
ac7bf12743 | ||
![]() |
db21e20c99 | ||
![]() |
56adb27b44 | ||
![]() |
520e96b6e8 | ||
![]() |
45599e7840 | ||
![]() |
8d9b0c42cb | ||
![]() |
7ab0dfc8ce | ||
![]() |
2c11095eed | ||
![]() |
07a0369b74 | ||
![]() |
cd253e470a | ||
![]() |
868a06eaf9 | ||
![]() |
512cd7b0de | ||
![]() |
352c598916 | ||
![]() |
661aee29da | ||
![]() |
0439df05cc | ||
![]() |
4333854293 | ||
![]() |
eb3baf7e99 | ||
![]() |
48a936ef5b | ||
![]() |
5a7d2be77d | ||
![]() |
15ff7c4cad | ||
![]() |
9ab9b97f20 | ||
![]() |
88d92aceab | ||
![]() |
a2ce4352c8 | ||
![]() |
310a146a55 | ||
![]() |
0e201ffdcc | ||
![]() |
d5d25d78da | ||
![]() |
84f43ccde8 | ||
![]() |
38704c9cf3 | ||
![]() |
910d0ec92b | ||
![]() |
3b05c89765 | ||
![]() |
e77b3fa46f | ||
![]() |
4f8b4c605e | ||
![]() |
b2fb920d28 | ||
![]() |
4f041694d3 | ||
![]() |
12147f6d58 | ||
![]() |
40bc60d6ae | ||
![]() |
7778210269 | ||
![]() |
6229210d51 | ||
![]() |
5d0d5b5d97 | ||
![]() |
1aa3c1e543 | ||
![]() |
b90e32fe4e | ||
![]() |
1f4df2a64d | ||
![]() |
2efc1db6a9 | ||
![]() |
51aa1d2db8 | ||
![]() |
2d2df25d04 | ||
![]() |
29eb3e9ebc | ||
![]() |
b0873fbc90 | ||
![]() |
c14484a5cc | ||
![]() |
a966cfeb1f | ||
![]() |
6c0546d829 | ||
![]() |
e1e8f45983 | ||
![]() |
3eedcc55b9 | ||
![]() |
dd2cab1488 | ||
![]() |
f883c09b6b | ||
![]() |
71acad6c21 | ||
![]() |
e2d4654e20 | ||
![]() |
2b8f1170a6 | ||
![]() |
5c4743441e | ||
![]() |
938054bdb8 | ||
![]() |
d0909adf6b | ||
![]() |
a12b004fa4 | ||
![]() |
a8452957fc | ||
![]() |
feb334756e | ||
![]() |
50a4d7169e | ||
![]() |
6239b6c0e2 | ||
![]() |
cb288439a4 | ||
![]() |
b789ffd2bf | ||
![]() |
69f741e8a6 | ||
![]() |
4b4f47002b | ||
![]() |
615c301961 | ||
![]() |
dc07180e48 | ||
![]() |
d3b235bab5 | ||
![]() |
0279f4fb57 | ||
![]() |
7c920ddebe | ||
![]() |
88793cbc1a | ||
![]() |
2afe427ab3 | ||
![]() |
bbc088ae4e | ||
![]() |
4c08c0b8b1 | ||
![]() |
fe195257d8 | ||
![]() |
57d5df8118 | ||
![]() |
59792cb0b8 | ||
![]() |
b864094fdc | ||
![]() |
3ef83cc34e | ||
![]() |
3f133dd586 | ||
![]() |
cc557c4d60 | ||
![]() |
0c98d93e9a | ||
![]() |
c344403bed | ||
![]() |
150e8f78bf | ||
![]() |
bc7fdba36d | ||
![]() |
fe3ab7b937 | ||
![]() |
458084d79b | ||
![]() |
f44bc19ce1 | ||
![]() |
d3947d0ad5 | ||
![]() |
849ed122c7 | ||
![]() |
254ee00c37 | ||
![]() |
594b97feb1 | ||
![]() |
cc0def15c4 | ||
![]() |
81154130b5 | ||
![]() |
0514f25c61 | ||
![]() |
2670bbdcc8 | ||
![]() |
bd3e096411 | ||
![]() |
270a74e53b | ||
![]() |
47d103e8a1 | ||
![]() |
188f7ab795 | ||
![]() |
4953a57c1c | ||
![]() |
199037c682 | ||
![]() |
64f84d5468 | ||
![]() |
047561dc22 | ||
![]() |
a542a0804a | ||
![]() |
cddeb2a0df | ||
![]() |
de1d443db1 | ||
![]() |
e2040ed395 | ||
![]() |
8a0ba7a725 | ||
![]() |
e74788ea32 | ||
![]() |
fef79931c5 | ||
![]() |
b52b0ac85a | ||
![]() |
9a30286289 | ||
![]() |
20310437d0 | ||
![]() |
d29e1544bf | ||
![]() |
ae4f4d3533 | ||
![]() |
89a18b49a7 | ||
![]() |
956c5faebb | ||
![]() |
eb589b0a46 | ||
![]() |
52eff41379 | ||
![]() |
45071607aa | ||
![]() |
f5d104e7af | ||
![]() |
4f8d2a8b1c | ||
![]() |
0158a2b6b9 | ||
![]() |
00b8ced09f | ||
![]() |
5f51be43cf | ||
![]() |
3e2de560ca | ||
![]() |
c266fb7758 | ||
![]() |
0d09f307b2 | ||
![]() |
eb7d321cb8 | ||
![]() |
cd0396c1f1 | ||
![]() |
79f9b268bb | ||
![]() |
b45f3c8deb | ||
![]() |
f8a8de87e4 | ||
![]() |
2183f0553c | ||
![]() |
7e14f8f830 | ||
![]() |
28e044a36a | ||
![]() |
4e91d8279b | ||
![]() |
ff3d8509ac | ||
![]() |
e861d4f83d | ||
![]() |
31d89b36cf | ||
![]() |
6b24344031 | ||
![]() |
c55e250c45 | ||
![]() |
cd241a93c1 | ||
![]() |
53acf7ae82 | ||
![]() |
c34f6ed8c0 | ||
![]() |
329c448d30 | ||
![]() |
a6619e9a13 | ||
![]() |
24ce5da2b8 | ||
![]() |
b4f751080d | ||
![]() |
6d02edebc9 | ||
![]() |
349882ed75 | ||
![]() |
4464310e74 | ||
![]() |
1f28790476 | ||
![]() |
c8dae95eff | ||
![]() |
547a084c7e | ||
![]() |
493677ff81 | ||
![]() |
6b430ba271 | ||
![]() |
4b3dcf831b | ||
![]() |
bc6924d303 | ||
![]() |
02b00f9146 | ||
![]() |
e807ed5870 | ||
![]() |
f08944253b | ||
![]() |
792d6584b9 | ||
![]() |
7b45d01462 | ||
![]() |
5c17b2966a | ||
![]() |
0c54f29446 | ||
![]() |
9c3cf39fdd | ||
![]() |
d2fb229685 | ||
![]() |
f55bc6682f | ||
![]() |
6857286b42 | ||
![]() |
c0d5bd2048 | ||
![]() |
666e5d7904 | ||
![]() |
3613407ac5 | ||
![]() |
c32dceb4d4 | ||
![]() |
5573e78364 | ||
![]() |
807a19889f | ||
![]() |
df7242de91 | ||
![]() |
d62426f168 | ||
![]() |
1714cf3417 | ||
![]() |
1080c917be | ||
![]() |
8eb3164878 | ||
![]() |
915c5442d1 | ||
![]() |
be0360d5e8 | ||
![]() |
4d6ae6ffdd | ||
![]() |
ecee6f415b | ||
![]() |
47680f936b | ||
![]() |
2d7181105d | ||
![]() |
9bdc75524b | ||
![]() |
2f6ceb4949 | ||
![]() |
cd933aa35f | ||
![]() |
138738075b | ||
![]() |
7dd65f3028 | ||
![]() |
2ee57f9b0d | ||
![]() |
bc56f8c2f0 | ||
![]() |
596ff7e6bf | ||
![]() |
e8667f99be | ||
![]() |
9b427b3171 | ||
![]() |
baff5e5594 | ||
![]() |
020c9b41cc | ||
![]() |
e975e2e477 | ||
![]() |
4e1dc562f7 | ||
![]() |
d097babe73 | ||
![]() |
8fa212f04d | ||
![]() |
b9c9a5f1dd | ||
![]() |
4fb8b45111 | ||
![]() |
6c107443d3 | ||
![]() |
67c6d111a8 | ||
![]() |
18ebd42c52 | ||
![]() |
9675cc77e2 | ||
![]() |
4ce1dae673 | ||
![]() |
d89136b09c | ||
![]() |
d58c38943a | ||
![]() |
66704ec879 | ||
![]() |
422c1e9288 | ||
![]() |
683f0da2e7 | ||
![]() |
c7a8fc91c0 | ||
![]() |
cfd255a014 | ||
![]() |
1c30b3d5a1 | ||
![]() |
4964eda167 | ||
![]() |
502e5f006a | ||
![]() |
23235e3194 | ||
![]() |
93834fe389 | ||
![]() |
bd32b229b5 | ||
![]() |
c7d2cb855a | ||
![]() |
04c924ae3b | ||
![]() |
6c8a85a391 | ||
![]() |
74780131bd | ||
![]() |
2c092d2613 | ||
![]() |
171b31ae67 | ||
![]() |
5b3abe2c9c | ||
![]() |
59186f1fb0 | ||
![]() |
5e68531428 | ||
![]() |
e7b15a9041 | ||
![]() |
853afa1bce | ||
![]() |
627fd755e8 | ||
![]() |
88a66df9d6 | ||
![]() |
af951dc08a | ||
![]() |
5a5655b790 | ||
![]() |
a6d20d1907 | ||
![]() |
b88d1e6820 | ||
![]() |
19d2864c34 | ||
![]() |
29e3a17f26 | ||
![]() |
252e9f736f | ||
![]() |
5d08988dda | ||
![]() |
47ca4246aa | ||
![]() |
f8338d4f00 | ||
![]() |
5cf6032c90 | ||
![]() |
8d8b77412d | ||
![]() |
d882c3361d | ||
![]() |
9d50306e2f | ||
![]() |
96f99aeb8f | ||
![]() |
ed7263ee3e | ||
![]() |
f32d752ccb | ||
![]() |
671b7e079f | ||
![]() |
e10b15010c | ||
![]() |
02fe857755 | ||
![]() |
ea3f044cd8 | ||
![]() |
e6bb6c59ec | ||
![]() |
6d23ac67f9 | ||
![]() |
ca46b4d7a7 | ||
![]() |
0727ee94c0 | ||
![]() |
21e4c25e61 | ||
![]() |
e921c0b40b | ||
![]() |
c05d4cddfb | ||
![]() |
c9723ee4b7 | ||
![]() |
ca90c75c61 | ||
![]() |
9976665cc7 | ||
![]() |
c5f037fa64 | ||
![]() |
5ec13c0b06 | ||
![]() |
60ca12e4bd | ||
![]() |
67fcf7d7c5 | ||
![]() |
c953ed48b7 | ||
![]() |
6440df60aa | ||
![]() |
b806b0a97f | ||
![]() |
10197a0041 | ||
![]() |
166885802a | ||
![]() |
4a97c45585 | ||
![]() |
0173d3b313 | ||
![]() |
aadd32c973 | ||
![]() |
0f4bf5569a | ||
![]() |
ff4cf6c6d1 | ||
![]() |
a7b7e35512 | ||
![]() |
11135b48e6 | ||
![]() |
d0382caa88 | ||
![]() |
4765726bda | ||
![]() |
455a412aaa | ||
![]() |
1a2b505979 | ||
![]() |
c34f3c9b94 | ||
![]() |
232084c2f9 | ||
![]() |
2ba092711f | ||
![]() |
8aa4227c0c | ||
![]() |
81afb47cd0 | ||
![]() |
60a3aae35f | ||
![]() |
bc3415ce8b | ||
![]() |
3f1acd3642 | ||
![]() |
4564d251a8 | ||
![]() |
8783ed1981 | ||
![]() |
1da09f5b1b | ||
![]() |
062df65b1e | ||
![]() |
899eaa3307 | ||
![]() |
5140eaa5e7 | ||
![]() |
cca20fec07 | ||
![]() |
12d67dad35 | ||
![]() |
8a68d085b4 | ||
![]() |
e437cc4faf | ||
![]() |
1fb858e2d7 | ||
![]() |
3945a3add9 | ||
![]() |
5348a446a7 | ||
![]() |
e8e33d5fc4 | ||
![]() |
c28580745b | ||
![]() |
e7b587d550 | ||
![]() |
5d34b9b5bb | ||
![]() |
86be7938f1 | ||
![]() |
7ad5a5efec | ||
![]() |
64f2735e60 | ||
![]() |
3a0a0facdf | ||
![]() |
ec66ee3bfb | ||
![]() |
ca9dd74fbf | ||
![]() |
2da847dd30 | ||
![]() |
4cb5c3782b | ||
![]() |
dcf39ee44e | ||
![]() |
f2cfa3e1c4 | ||
![]() |
0c2c20254b | ||
![]() |
193d6a4fd4 | ||
![]() |
acfeec7a5d | ||
![]() |
45d908e25f | ||
![]() |
fd9114e7e2 | ||
![]() |
b4f454a553 | ||
![]() |
299f813e28 | ||
![]() |
eedd490e2d | ||
![]() |
0a8aca516a | ||
![]() |
70808bde64 | ||
![]() |
93bf0fc547 | ||
![]() |
95e7f8b1c8 | ||
![]() |
e2d6bb7444 | ||
![]() |
f89916e6fb | ||
![]() |
7d95b15cbc | ||
![]() |
27e78c71e0 | ||
![]() |
8333927737 | ||
![]() |
db03db0dca | ||
![]() |
bd96f6e572 | ||
![]() |
04041f9583 | ||
![]() |
826d1b207e | ||
![]() |
f92bae887f | ||
![]() |
d2983b7fde | ||
![]() |
059955a48c | ||
![]() |
6ebac6a0b2 | ||
![]() |
b5a9d0654e | ||
![]() |
242ba727b2 | ||
![]() |
d5db4ca0e7 | ||
![]() |
d256d3dabe | ||
![]() |
759da033fc | ||
![]() |
c074338f4c | ||
![]() |
3dd2434149 | ||
![]() |
3699514d18 | ||
![]() |
f045cf43e4 | ||
![]() |
843dad19e9 | ||
![]() |
73e2ac4211 | ||
![]() |
6d113de1f8 | ||
![]() |
2e6f115bcc | ||
![]() |
a3fba2f8f7 | ||
![]() |
e2b671f1b2 | ||
![]() |
2a35fbe29e | ||
![]() |
81cde72fd0 | ||
![]() |
bf9ffba4f7 | ||
![]() |
c975d8b943 | ||
![]() |
97f78059a2 | ||
![]() |
fd47edb905 | ||
![]() |
8ee442e9b1 | ||
![]() |
2730f91872 | ||
![]() |
97ca85e155 | ||
![]() |
39bb4c5871 | ||
![]() |
bdceb90c59 | ||
![]() |
6633c7fd42 | ||
![]() |
01b32d5ee0 | ||
![]() |
1260a0147a | ||
![]() |
ef54b7d9de | ||
![]() |
f66315d2de | ||
![]() |
b50173ae8b | ||
![]() |
b37c031fd1 | ||
![]() |
84e5da4bf0 | ||
![]() |
3bb7693200 | ||
![]() |
b22c00d0cd | ||
![]() |
7006b075c3 | ||
![]() |
774024a41b | ||
![]() |
5fb97b81d1 | ||
![]() |
8bd1b5228c | ||
![]() |
23dd613ff9 | ||
![]() |
bb7be9a4cd | ||
![]() |
570755f05a | ||
![]() |
957d3e51e0 | ||
![]() |
fc6c274c97 | ||
![]() |
313b092ba8 | ||
![]() |
0e9e213324 | ||
![]() |
86e6f4fcc0 | ||
![]() |
a009e95afd | ||
![]() |
32aafb3572 | ||
![]() |
b577783cf0 | ||
![]() |
aa7b872a14 | ||
![]() |
c6f7f57776 | ||
![]() |
122db76781 | ||
![]() |
106ad08cd2 | ||
![]() |
0341ca1b6a | ||
![]() |
7581ea55db | ||
![]() |
fc9cee38d8 | ||
![]() |
b175e4128d | ||
![]() |
97b07798b0 | ||
![]() |
112fcd206d | ||
![]() |
11d1f56062 | ||
![]() |
bd840d4638 | ||
![]() |
601e5e6abc | ||
![]() |
6bacb23002 | ||
![]() |
5c300a9f1a | ||
![]() |
6e1500c251 | ||
![]() |
c3d393f214 | ||
![]() |
f88fc0ca1a | ||
![]() |
fb8d8242ab | ||
![]() |
f2a3dfd700 | ||
![]() |
9e1b24f3a1 | ||
![]() |
58a345d346 | ||
![]() |
7ed67d216b | ||
![]() |
3ae660ca90 | ||
![]() |
a742e1fc71 | ||
![]() |
85f9863e0a | ||
![]() |
ce88dee14d | ||
![]() |
83572701f4 | ||
![]() |
fa7d7e9187 | ||
![]() |
f818cde32c | ||
![]() |
9da93cd887 | ||
![]() |
026e7ea32a | ||
![]() |
9659d19718 | ||
![]() |
50d35c9677 | ||
![]() |
4260e78861 | ||
![]() |
7342ae2e33 | ||
![]() |
35dbc1a90c | ||
![]() |
c7a4355153 | ||
![]() |
33a84a8ca2 | ||
![]() |
1d04490ed3 | ||
![]() |
4a30c2d79c | ||
![]() |
83072d6b9c | ||
![]() |
c779fc37eb | ||
![]() |
e08c13ad7e | ||
![]() |
2c82a6b2e0 | ||
![]() |
3929f17aef | ||
![]() |
ee39af3419 | ||
![]() |
3882a5a263 | ||
![]() |
a360475c7b | ||
![]() |
ac06088948 | ||
![]() |
a757eebfbb | ||
![]() |
2be4f89555 | ||
![]() |
3a3f605a56 | ||
![]() |
407fa2720a | ||
![]() |
4a5c7d8261 | ||
![]() |
f591193dda | ||
![]() |
434869900e | ||
![]() |
2aed7378cc | ||
![]() |
71cd6e6248 | ||
![]() |
c83294916a | ||
![]() |
603bbe0afd | ||
![]() |
c361e235eb | ||
![]() |
8a59493d96 | ||
![]() |
7ef86cbf9f | ||
![]() |
c9530118a4 | ||
![]() |
878d9abeb7 | ||
![]() |
2d705efe1c | ||
![]() |
aeaef85507 | ||
![]() |
ebae25d175 | ||
![]() |
5ad1a01d7a | ||
![]() |
8f84e1befd | ||
![]() |
7293b32025 | ||
![]() |
fed8f12863 | ||
![]() |
9975905faf | ||
![]() |
718ae433b2 | ||
![]() |
ed65f52f50 | ||
![]() |
ba1d86ec80 | ||
![]() |
7661c408a4 | ||
![]() |
ee8d5f18ef | ||
![]() |
de3b9b8232 | ||
![]() |
8e99448819 | ||
![]() |
1e548fb6e3 | ||
![]() |
fdc0329e64 | ||
![]() |
d3db0400b0 | ||
![]() |
b1096a9935 | ||
![]() |
ab5b6f83fd | ||
![]() |
2172aaf1ce | ||
![]() |
c68dbc4e5c | ||
![]() |
ec961f26e9 | ||
![]() |
c3be961ccf | ||
![]() |
166ce0da5a | ||
![]() |
edbaea8df2 | ||
![]() |
af3a625f64 | ||
![]() |
11d24a583d | ||
![]() |
e9e3d8c57c | ||
![]() |
5588291a35 | ||
![]() |
4b41e766c6 | ||
![]() |
90d52b6501 | ||
![]() |
233184568c | ||
![]() |
59da778009 | ||
![]() |
ad4cf79cc9 | ||
![]() |
4f3828237a | ||
![]() |
946cf25732 | ||
![]() |
108ce95b7c | ||
![]() |
86e9ed5f3a | ||
![]() |
fbecb05bf4 | ||
![]() |
be72d45356 | ||
![]() |
219c416a1e | ||
![]() |
16f7ec9950 | ||
![]() |
d79bf853b1 | ||
![]() |
1ae6378d85 | ||
![]() |
089a843abd | ||
![]() |
aea4d3c4b0 | ||
![]() |
4983703375 | ||
![]() |
3856224df9 | ||
![]() |
6d4bedfc56 | ||
![]() |
dda0dfc140 | ||
![]() |
e0f56b9e2d | ||
![]() |
3c3c3eeeca | ||
![]() |
31c7151580 | ||
![]() |
50a617764a | ||
![]() |
7d67c87a47 | ||
![]() |
c072902f23 | ||
![]() |
5ad4f3c54b | ||
![]() |
0b6055954d | ||
![]() |
80c177e9a0 | ||
![]() |
cdcef49eef | ||
![]() |
9e18aafccc | ||
![]() |
f7346c1d78 | ||
![]() |
b709401814 | ||
![]() |
4074db5f50 | ||
![]() |
8c6bc02bf7 | ||
![]() |
35c11afd54 | ||
![]() |
c884e2f285 | ||
![]() |
b6ba17a865 | ||
![]() |
bea821f194 | ||
![]() |
713c7585b5 | ||
![]() |
f12c25b7ae | ||
![]() |
0a54b987a1 | ||
![]() |
2240327286 | ||
![]() |
4e276256c0 | ||
![]() |
866e7ff3ce | ||
![]() |
d0f9062b56 | ||
![]() |
2cafbb2aba | ||
![]() |
b78c64376f | ||
![]() |
2518612b1b | ||
![]() |
dda521a150 | ||
![]() |
95a155b10d | ||
![]() |
2384a240e0 | ||
![]() |
b9cc036703 | ||
![]() |
4e9b88559b | ||
![]() |
3452682a42 | ||
![]() |
9262b24504 | ||
![]() |
a5fa43b526 | ||
![]() |
8681a3d74c | ||
![]() |
f9c4d88b12 | ||
![]() |
799032505e | ||
![]() |
c8f174ac92 | ||
![]() |
047e169f3e | ||
![]() |
687327c9e8 | ||
![]() |
26dc37bd76 | ||
![]() |
c693e4aa64 | ||
![]() |
acab731fef | ||
![]() |
7e4ba3cb72 | ||
![]() |
172c4d9c7d | ||
![]() |
bd5f6cbc7b | ||
![]() |
6fcd1c734b | ||
![]() |
eca097dbfb | ||
![]() |
51ffafa011 | ||
![]() |
8dca602346 | ||
![]() |
0ed24f3a05 | ||
![]() |
e25e0030e7 | ||
![]() |
df4b6b92f2 | ||
![]() |
1c69913eca | ||
![]() |
cb5c6259fd | ||
![]() |
bf287fefb5 | ||
![]() |
20bf1d68e6 | ||
![]() |
9bc4c168fd | ||
![]() |
3415049d1c | ||
![]() |
a45949b597 | ||
![]() |
6009d4abab | ||
![]() |
16fb843c9b | ||
![]() |
36b333459b | ||
![]() |
4d3320233e | ||
![]() |
933a1a41e6 | ||
![]() |
1ff8626716 | ||
![]() |
c30466b84a | ||
![]() |
868f1a4431 | ||
![]() |
05f529fffd | ||
![]() |
f01388559f | ||
![]() |
27edd4a610 | ||
![]() |
cc421b04cd | ||
![]() |
3f2bc325a1 | ||
![]() |
54686dfd79 | ||
![]() |
f22cf02ed8 | ||
![]() |
5b51d0f733 | ||
![]() |
e03f82636a | ||
![]() |
d53d85bd79 | ||
![]() |
4682ae0898 | ||
![]() |
fd5b195879 | ||
![]() |
bb5df9839d | ||
![]() |
be34d55291 | ||
![]() |
c13911b171 | ||
![]() |
6f83bdd6f3 | ||
![]() |
9bcd425a85 | ||
![]() |
ec917f70d2 | ||
![]() |
40ce4eeb43 | ||
![]() |
29ae84e199 | ||
![]() |
250011f016 | ||
![]() |
e08c85ae2d | ||
![]() |
dcb5ca203c | ||
![]() |
77df5a8f24 | ||
![]() |
d6bebd2507 | ||
![]() |
f74996c02f | ||
![]() |
eea2d35d3a | ||
![]() |
d94e8bd82d | ||
![]() |
b0c92e1a34 | ||
![]() |
ead5bcf048 | ||
![]() |
bdd268a524 | ||
![]() |
e783c2bd2c | ||
![]() |
837fc98638 | ||
![]() |
5deca66fdc | ||
![]() |
cfe2dd4147 | ||
![]() |
00f8d65a17 | ||
![]() |
4e0e4c00bf | ||
![]() |
a8c77a6fba | ||
![]() |
31aa6d0c4f | ||
![]() |
d051c4931d | ||
![]() |
94b0baceb0 | ||
![]() |
16feb261e2 | ||
![]() |
f084bf7872 | ||
![]() |
1112d3907a | ||
![]() |
3464497880 | ||
![]() |
651f57bced | ||
![]() |
b4e72aba6c | ||
![]() |
061dd2dfef | ||
![]() |
5f4ec7de5b | ||
![]() |
6f81bb4b09 | ||
![]() |
4ed60a5711 | ||
![]() |
c93195c94b | ||
![]() |
f30adac4bb | ||
![]() |
a4e4217204 | ||
![]() |
8754d705a1 | ||
![]() |
23d4a2d6a5 | ||
![]() |
ce77b148d9 | ||
![]() |
be3eca39e8 | ||
![]() |
3413b1aeb4 | ||
![]() |
356d13e9dd | ||
![]() |
fa34bf0aaf | ||
![]() |
5d0941476a | ||
![]() |
5ff0bbd0f8 | ||
![]() |
a3764e533c | ||
![]() |
3e05cba30e | ||
![]() |
14b3c0f0af | ||
![]() |
67aff05051 | ||
![]() |
19a101c3ac | ||
![]() |
8da17a8211 | ||
![]() |
2748929039 | ||
![]() |
0c900a4bfa | ||
![]() |
f1d5d70010 | ||
![]() |
56ebc7637d | ||
![]() |
996dd9fc8b | ||
![]() |
056514d598 | ||
![]() |
9a21bdfd6a | ||
![]() |
03f99dd26e | ||
![]() |
bfb1b641f9 | ||
![]() |
72ba98c464 | ||
![]() |
dcd19c0592 | ||
![]() |
109159e0f7 | ||
![]() |
409b877eea | ||
![]() |
c5bf7948ff | ||
![]() |
b9f7127691 | ||
![]() |
1e6f5f012c | ||
![]() |
225d85fd9b | ||
![]() |
1bb22f118d | ||
![]() |
552c30eae4 | ||
![]() |
48e8a26813 | ||
![]() |
ade847bc89 | ||
![]() |
a6173e0eae | ||
![]() |
4529bb4a83 | ||
![]() |
258ecb764f | ||
![]() |
6f595e9abb | ||
![]() |
35c4c7e8bf | ||
![]() |
293ed924d1 | ||
![]() |
c8121176b3 | ||
![]() |
ee270f9b00 | ||
![]() |
bf1d77a4d8 | ||
![]() |
a9344fafe9 | ||
![]() |
b8890726f2 | ||
![]() |
0f84332654 | ||
![]() |
46c82259f7 | ||
![]() |
2d03823283 | ||
![]() |
bba144eca5 | ||
![]() |
9af73dad93 | ||
![]() |
f0d66bf6a6 | ||
![]() |
5ad53a7554 | ||
![]() |
7b2e3331f2 | ||
![]() |
3cb44f6652 | ||
![]() |
b7fdff46f2 | ||
![]() |
e16109330d | ||
![]() |
72621531e0 | ||
![]() |
0a48146efc | ||
![]() |
0c4bf12bfd | ||
![]() |
b8e0855ef3 | ||
![]() |
6467502b9d | ||
![]() |
15b67f20e5 | ||
![]() |
0825179f00 | ||
![]() |
97211d0aad | ||
![]() |
029c499bfa | ||
![]() |
0ba867ec16 | ||
![]() |
866d147122 | ||
![]() |
32851d1bc7 | ||
![]() |
78257408b4 | ||
![]() |
f447b7615e | ||
![]() |
1f780b7209 | ||
![]() |
04bf8a6b1a | ||
![]() |
c4c64854d4 | ||
![]() |
17562dc90b | ||
![]() |
7b24316734 | ||
![]() |
5fab107fd3 | ||
![]() |
f31920e092 | ||
![]() |
eb111a10e7 | ||
![]() |
80b09360c6 | ||
![]() |
5ccf78855d | ||
![]() |
fd5a3b5880 | ||
![]() |
6120c1360c | ||
![]() |
a8087dc12c | ||
![]() |
070c03dbf7 | ||
![]() |
0a9bec3754 | ||
![]() |
fff25ac753 | ||
![]() |
4f1e79b6b8 | ||
![]() |
aa9933c0b5 | ||
![]() |
0697d1f859 | ||
![]() |
df033fa4aa | ||
![]() |
b941a7df83 | ||
![]() |
31151cec3c | ||
![]() |
07e8c338df | ||
![]() |
b22d7218aa | ||
![]() |
d5be8c74b0 | ||
![]() |
c112cb60da | ||
![]() |
677fa4f9bc | ||
![]() |
907af2ad02 | ||
![]() |
6a2e7bbc02 | ||
![]() |
771c46032f | ||
![]() |
85611aa456 | ||
![]() |
466b5cb08d | ||
![]() |
3f2f3251cb | ||
![]() |
8ae85f3991 | ||
![]() |
781fe4ff28 | ||
![]() |
163c59128e | ||
![]() |
608896571c | ||
![]() |
2e5ca1cbd2 | ||
![]() |
680fb51c37 | ||
![]() |
77d74b404e | ||
![]() |
a636d2127a | ||
![]() |
9a766f4cd9 | ||
![]() |
8ad17d25ef | ||
![]() |
46d00dd85f | ||
![]() |
ec52b13449 | ||
![]() |
cf6ca1b0ba | ||
![]() |
37bd6de658 | ||
![]() |
f7622ca332 | ||
![]() |
b82b56970b | ||
![]() |
e4eb5b79c9 | ||
![]() |
1cfea20b22 | ||
![]() |
efa3ffa8d8 | ||
![]() |
1b8c94d6b9 | ||
![]() |
cd5c1f3f45 | ||
![]() |
9200fa6d06 | ||
![]() |
1bbe9896f6 | ||
![]() |
2d8847f428 | ||
![]() |
72f6e018e7 | ||
![]() |
2fbbd540bb | ||
![]() |
18f64b5fb7 | ||
![]() |
d2a8b1e8a5 | ||
![]() |
184e8eca7c | ||
![]() |
0a8886704a | ||
![]() |
0712314d23 | ||
![]() |
f8cbba1850 | ||
![]() |
635ec3ce37 | ||
![]() |
8e71130e8a | ||
![]() |
ed7baf3ae1 | ||
![]() |
1e159af2ce | ||
![]() |
dffed6e393 | ||
![]() |
bf656af555 | ||
![]() |
d2b8852d19 | ||
![]() |
7d4de71899 | ||
![]() |
e1c16d78e4 | ||
![]() |
a49b49cba7 | ||
![]() |
f510564d9d | ||
![]() |
1c4b484a56 | ||
![]() |
b394d8d059 | ||
![]() |
a15c1c71d5 | ||
![]() |
8d679e7e00 | ||
![]() |
2b30ac2351 | ||
![]() |
1c97793b49 | ||
![]() |
4dae8b41da | ||
![]() |
be8ed2f59e | ||
![]() |
08491fcd86 | ||
![]() |
abed633fcb | ||
![]() |
db2a9cb6d5 | ||
![]() |
7caeb3b0d8 | ||
![]() |
08500be239 | ||
![]() |
3ef7d8fecb | ||
![]() |
ffde7223b9 | ||
![]() |
4e84fa4a00 | ||
![]() |
78e49928b6 | ||
![]() |
c0bcfe244c | ||
![]() |
e63ecd81ec | ||
![]() |
c47a858d15 | ||
![]() |
076c9a0dd9 | ||
![]() |
3993176b76 | ||
![]() |
16cad48641 | ||
![]() |
7a6d0c2efc | ||
![]() |
f6035f2dda | ||
![]() |
c34a1e29de | ||
![]() |
41a69027c2 | ||
![]() |
711c614528 | ||
![]() |
6acb240f69 | ||
![]() |
45f3dd8b7a | ||
![]() |
acc1bd6297 | ||
![]() |
49ed9dae34 | ||
![]() |
cf554d306d | ||
![]() |
ef24cfa523 | ||
![]() |
5d35983298 | ||
![]() |
2dacb36789 | ||
![]() |
57a1403f08 | ||
![]() |
bad3283182 | ||
![]() |
6ed4aff4d3 | ||
![]() |
e525465592 | ||
![]() |
10782f4c84 | ||
![]() |
2a02576d6d | ||
![]() |
9ea1578a97 | ||
![]() |
520028dcfc | ||
![]() |
e98cef06c7 | ||
![]() |
aef0535c55 | ||
![]() |
6b1d0cb01d | ||
![]() |
f23ecf00da | ||
![]() |
a1c1e26875 | ||
![]() |
410b8711f2 | ||
![]() |
6acf81d5ae | ||
![]() |
4eb56d844e | ||
![]() |
5faf6d061f | ||
![]() |
d5a9f6d79d | ||
![]() |
2699889342 | ||
![]() |
e4f933361e | ||
![]() |
6f278977e9 | ||
![]() |
4f2f705dca | ||
![]() |
f31e38145d | ||
![]() |
0231622169 | ||
![]() |
937423dbcf | ||
![]() |
40483d8478 | ||
![]() |
6ec5089cc9 | ||
![]() |
15f419e1cb | ||
![]() |
bdd8c34c67 | ||
![]() |
c9a9248c9f | ||
![]() |
31f7fede30 | ||
![]() |
917fe549b0 | ||
![]() |
8e430e55af | ||
![]() |
9e61bda592 | ||
![]() |
56997290d7 | ||
![]() |
d2f84f3df8 | ||
![]() |
9da28e5c73 | ||
![]() |
d1f9b06f84 | ||
![]() |
f9f3306db9 | ||
![]() |
19d19cd737 | ||
![]() |
b1175acb59 | ||
![]() |
672278e5fd | ||
![]() |
da155f8822 | ||
![]() |
4026ef63b6 | ||
![]() |
b282682ba5 | ||
![]() |
ad00926e1b | ||
![]() |
0b774df375 | ||
![]() |
53ffcf455c | ||
![]() |
9ca64d5fb3 | ||
![]() |
bd79354f32 | ||
![]() |
49dcac5c21 | ||
![]() |
38a4b0d8d5 | ||
![]() |
a224225e48 | ||
![]() |
7d7fe756b3 | ||
![]() |
1cb7fe12ff | ||
![]() |
8a29805767 | ||
![]() |
94c196108d | ||
![]() |
263d1ba002 | ||
![]() |
2dba06dc34 | ||
![]() |
811860c3b4 | ||
![]() |
8439119e24 | ||
![]() |
b5b40d8235 | ||
![]() |
b904f8af03 | ||
![]() |
ebfbb74f9e | ||
![]() |
7b4225aa1f | ||
![]() |
71a5311b06 | ||
![]() |
a62a35e1db | ||
![]() |
ca2439f595 | ||
![]() |
f9a0db716a | ||
![]() |
34aa67ea87 | ||
![]() |
18be8c3318 | ||
![]() |
1d7a8f992f | ||
![]() |
da1783cdff | ||
![]() |
20d74bb07e | ||
![]() |
0f7a0b04ca | ||
![]() |
40c6a214e3 | ||
![]() |
cfe024ea13 | ||
![]() |
993d85125e | ||
![]() |
bedcf1cce5 | ||
![]() |
30e3ef4c8e | ||
![]() |
4c5fea96e4 | ||
![]() |
46600931e4 | ||
![]() |
a2387210bf | ||
![]() |
d7e7adb496 | ||
![]() |
45354a421c | ||
![]() |
9fc3c60910 | ||
![]() |
1976003e91 | ||
![]() |
488afc47d4 | ||
![]() |
017814adc7 | ||
![]() |
7f94af8b2c | ||
![]() |
09d74f05c3 | ||
![]() |
1af8694ef6 | ||
![]() |
b8eb9b466a | ||
![]() |
bd9e449b69 | ||
![]() |
f3d67115d7 | ||
![]() |
ee6603ed38 | ||
![]() |
0dacde32f2 | ||
![]() |
528e05f025 | ||
![]() |
269583f5dd | ||
![]() |
7c9f4f7e4f | ||
![]() |
00fd692eba | ||
![]() |
668c3782b2 | ||
![]() |
1e0af2dadf | ||
![]() |
4ea2ea2a52 | ||
![]() |
8a243e6e28 | ||
![]() |
d33aa01000 | ||
![]() |
bd893e6336 | ||
![]() |
a74b07728e | ||
![]() |
7d69cbbda7 | ||
![]() |
955502f881 | ||
![]() |
dee5d1b87b | ||
![]() |
d42342e0ba | ||
![]() |
8da3f8c6a7 | ||
![]() |
c8c553c75c | ||
![]() |
c97aabe43a | ||
![]() |
b77acd35f7 | ||
![]() |
4873159872 | ||
![]() |
968624035c | ||
![]() |
b838bf3109 | ||
![]() |
4d1ce7023b | ||
![]() |
52577ac87a | ||
![]() |
239698cb5a | ||
![]() |
e55de6e9f0 | ||
![]() |
cfaf2ed03c | ||
![]() |
6015960871 | ||
![]() |
26328cc915 | ||
![]() |
cd512f0b40 | ||
![]() |
be14dd59a8 | ||
![]() |
97e5787ff7 | ||
![]() |
6975d3ca4b | ||
![]() |
cdca27e6bb | ||
![]() |
5b775ca5b4 | ||
![]() |
ea95da3b1a | ||
![]() |
57687779be | ||
![]() |
d39b11ba5d | ||
![]() |
b29a43b4d7 | ||
![]() |
f60a42e0b6 | ||
![]() |
85b0029ba2 | ||
![]() |
0e0f46a1e0 | ||
![]() |
6f539cfcd6 | ||
![]() |
0185d58a2b | ||
![]() |
d7df5e1c90 | ||
![]() |
e4e4576a39 | ||
![]() |
f789451007 | ||
![]() |
36680607d0 | ||
![]() |
fc54877c6b | ||
![]() |
6af7be4a45 | ||
![]() |
220d2bf026 | ||
![]() |
9ef1cf15a9 | ||
![]() |
679b3bc00f | ||
![]() |
1f9e32c35e | ||
![]() |
36410daaa4 | ||
![]() |
38bfef7d0b | ||
![]() |
724754f16c | ||
![]() |
4d32454697 | ||
![]() |
4db882f666 | ||
![]() |
a83bf97b98 | ||
![]() |
262e1957b7 | ||
![]() |
792411384d | ||
![]() |
78b0ff83e8 | ||
![]() |
23613355f3 | ||
![]() |
0d97eba7a5 | ||
![]() |
18efda719e | ||
![]() |
42239a30eb | ||
![]() |
43a9dc7082 | ||
![]() |
6f64fa070d | ||
![]() |
dc5b9d989b | ||
![]() |
9e407f5989 | ||
![]() |
fec6aac0f1 | ||
![]() |
541c31c879 | ||
![]() |
4ee0a06e18 | ||
![]() |
4f75eb9bfe | ||
![]() |
d2bd12822f | ||
![]() |
8a9d678bac | ||
![]() |
0440c41cba | ||
![]() |
a9c704b76e | ||
![]() |
0cca1b138c | ||
![]() |
d3576a1b71 | ||
![]() |
96707c0426 | ||
![]() |
e016cc8940 | ||
![]() |
34f636ffc3 | ||
![]() |
a134f692bf | ||
![]() |
d747576793 | ||
![]() |
d9578f6427 | ||
![]() |
b2cec7a0a3 | ||
![]() |
85db2d6704 | ||
![]() |
22ebb2bdd5 | ||
![]() |
e108568082 | ||
![]() |
360381e65d | ||
![]() |
3ead778664 | ||
![]() |
4fc08e39b4 | ||
![]() |
a2bdac571a | ||
![]() |
87fa6bca54 | ||
![]() |
c3226a3195 | ||
![]() |
51671af5a4 | ||
![]() |
2908f6565b | ||
![]() |
a0334d1d94 | ||
![]() |
423f2df5e0 | ||
![]() |
0122dc8452 | ||
![]() |
95ad1b0cc6 | ||
![]() |
52f46b94e9 | ||
![]() |
e07e0bc9c1 | ||
![]() |
4a1c231734 | ||
![]() |
fd0e958e95 | ||
![]() |
3d814115c8 | ||
![]() |
ca726a0110 | ||
![]() |
e01710cbd1 | ||
![]() |
c87a4a7d08 | ||
![]() |
b59170b702 | ||
![]() |
a237db556a | ||
![]() |
285ba54fe5 | ||
![]() |
ee86434a89 | ||
![]() |
95d5efbfe6 | ||
![]() |
c33f206ce8 | ||
![]() |
2d95ac2e94 | ||
![]() |
f58c14a74a | ||
![]() |
a52ce7bb7b | ||
![]() |
16d187b7ed | ||
![]() |
296ec4d07c | ||
![]() |
6e58fd1583 | ||
![]() |
c5fec4ac2a | ||
![]() |
fe2ca1ddef | ||
![]() |
e960626804 | ||
![]() |
7dd2dce6ad | ||
![]() |
a7ba10423d | ||
![]() |
8f1e7385b7 | ||
![]() |
25354b9d8c | ||
![]() |
ee720064a7 | ||
![]() |
e1b62fb90d | ||
![]() |
422cf5f182 | ||
![]() |
ef1acb4e2f | ||
![]() |
d4bbb8c851 | ||
![]() |
428f769c38 | ||
![]() |
133c8834df | ||
![]() |
99217593bf | ||
![]() |
1c6e4a2b18 | ||
![]() |
a6eb264770 | ||
![]() |
f5f296b13a | ||
![]() |
0091c4e12b | ||
![]() |
80172e17ac | ||
![]() |
2d96b05403 | ||
![]() |
ec0c1f0d02 | ||
![]() |
946b3c1f80 | ||
![]() |
a0dc398f36 | ||
![]() |
b54d2d984a | ||
![]() |
4ab73f9de9 | ||
![]() |
5ebe23e4bb | ||
![]() |
aa227cded1 | ||
![]() |
e406bdbb80 | ||
![]() |
1048f23680 | ||
![]() |
8fe8f09027 | ||
![]() |
78670c0941 | ||
![]() |
34f735890e | ||
![]() |
f08810b202 | ||
![]() |
7a68775e6c | ||
![]() |
e4fccc85c8 | ||
![]() |
2efa142ec9 | ||
![]() |
29b49dd630 | ||
![]() |
9d6bf7e720 | ||
![]() |
5f34508aae | ||
![]() |
2d8ecd561b | ||
![]() |
2059195ae9 | ||
![]() |
d89856f77b | ||
![]() |
975d5be046 | ||
![]() |
b01ef1b9a6 | ||
![]() |
ceb76b6a82 | ||
![]() |
a7e697b588 | ||
![]() |
3ecd918442 | ||
![]() |
4fbdb3a2d5 | ||
![]() |
0157643667 | ||
![]() |
fe741bd767 | ||
![]() |
06b9bdba2c | ||
![]() |
bd0aa74bdd | ||
![]() |
47461df59c | ||
![]() |
04d5588fe5 | ||
![]() |
40d061621b | ||
![]() |
a312629aad | ||
![]() |
d527d4b530 | ||
![]() |
978d2638d8 | ||
![]() |
cfcafdf822 | ||
![]() |
07865d0707 | ||
![]() |
1ac16516a1 | ||
![]() |
75e8795e3f | ||
![]() |
4912466d50 | ||
![]() |
664674913e | ||
![]() |
31e3658823 | ||
![]() |
abd416735d | ||
![]() |
6090bd2095 | ||
![]() |
1777592ec0 | ||
![]() |
8e8fbe14b1 | ||
![]() |
a8a39b6a38 | ||
![]() |
f84cb6de5e | ||
![]() |
dfc67c45c7 | ||
![]() |
e875da5d38 | ||
![]() |
9b9522e3f5 | ||
![]() |
87963685fb | ||
![]() |
0405a57f26 | ||
![]() |
f29c69d6a9 | ||
![]() |
7ec4de841e | ||
![]() |
1f08d2d03c | ||
![]() |
c1a695d1ac | ||
![]() |
ec05056e38 | ||
![]() |
c0b9339d31 | ||
![]() |
00a1731085 | ||
![]() |
6e3da00874 | ||
![]() |
38df01b266 | ||
![]() |
c729f16dcd | ||
![]() |
81d0c04ed4 | ||
![]() |
0924b63e10 | ||
![]() |
ce6afe9379 | ||
![]() |
6f04b2230a | ||
![]() |
8d90b831e1 | ||
![]() |
d4710604c4 | ||
![]() |
9c8da03c5c | ||
![]() |
85adefd9a4 | ||
![]() |
22804cfbe8 | ||
![]() |
8a4b88a59d | ||
![]() |
d2371af120 | ||
![]() |
aa2e1bb310 | ||
![]() |
6153fca4fc | ||
![]() |
f090af0a22 | ||
![]() |
58f420fdca | ||
![]() |
ded2b31fbc | ||
![]() |
75c8d2235b | ||
![]() |
f679961564 | ||
![]() |
471c37be59 | ||
![]() |
157ddcbab1 | ||
![]() |
ab160aa359 | ||
![]() |
ecc07e4e98 | ||
![]() |
cbc830fd65 | ||
![]() |
98a9f81d61 | ||
![]() |
f5460b35a3 | ||
![]() |
3456b1e50d | ||
![]() |
fe6abe1750 | ||
![]() |
6cdb3ff21e | ||
![]() |
01af2778ab | ||
![]() |
ad03c70753 | ||
![]() |
7fe0095fa7 | ||
![]() |
a4b236348f | ||
![]() |
aa40aae5bd | ||
![]() |
5a16e3ffa3 | ||
![]() |
d1957b83c8 | ||
![]() |
1b4fd74575 | ||
![]() |
def962b6cb | ||
![]() |
e802f1f61a | ||
![]() |
271b287356 | ||
![]() |
2a30acd99c | ||
![]() |
a8e70f18fd | ||
![]() |
ddd9f20a0b | ||
![]() |
f4a5d671fe | ||
![]() |
c72006dbcc | ||
![]() |
06fe30e2bd | ||
![]() |
08e76815ba | ||
![]() |
33ac3eb551 | ||
![]() |
d56a51cb5e | ||
![]() |
065a0c09f8 | ||
![]() |
04731fb7cc | ||
![]() |
12ff5a547f | ||
![]() |
9b2eb74f95 | ||
![]() |
84084baa65 | ||
![]() |
3bc45fbf68 | ||
![]() |
36168a24f5 | ||
![]() |
5e67443a1a | ||
![]() |
17858143b3 | ||
![]() |
c44a7b2705 | ||
![]() |
0ded23591b | ||
![]() |
c1a7aa652d | ||
![]() |
8d47f51399 | ||
![]() |
a81c9bfb81 | ||
![]() |
1caf57644f | ||
![]() |
c70b63c183 | ||
![]() |
1b89b4ef83 | ||
![]() |
8279cafd6d | ||
![]() |
014c2a82bd | ||
![]() |
594dfe572b | ||
![]() |
906e82f600 | ||
![]() |
bcb7e954e9 | ||
![]() |
866c87c65e | ||
![]() |
4ba36d7cb9 | ||
![]() |
13f8a912e3 | ||
![]() |
51f110a990 | ||
![]() |
17eae74c1c | ||
![]() |
cd4b673b6c | ||
![]() |
0d606c743b | ||
![]() |
81ea749248 | ||
![]() |
e009ad1a72 | ||
![]() |
abbd980671 | ||
![]() |
937da63ba6 | ||
![]() |
1f312b2e42 | ||
![]() |
1e3089ffb7 | ||
![]() |
5d7ff150dd | ||
![]() |
c767501c12 | ||
![]() |
00602d28a4 | ||
![]() |
7a56837141 | ||
![]() |
ed1caffc79 | ||
![]() |
65473b5113 | ||
![]() |
178d115ccb | ||
![]() |
10e5b0759c | ||
![]() |
0a81e462db | ||
![]() |
5cbbe8ae2e | ||
![]() |
00fafa16c7 | ||
![]() |
cea8db7eaa | ||
![]() |
b56c0e69e4 | ||
![]() |
b27e82e4a9 | ||
![]() |
ad48834469 | ||
![]() |
9d6b5e2ba1 | ||
![]() |
33ba190bec | ||
![]() |
3783350d25 | ||
![]() |
173405a343 | ||
![]() |
7bc1c9925b | ||
![]() |
ce4c69dd95 | ||
![]() |
8eea825462 | ||
![]() |
49e1ce7c43 | ||
![]() |
618f94f589 | ||
![]() |
ad2c22844c | ||
![]() |
b8df851414 | ||
![]() |
a584141cae | ||
![]() |
4e88f95f94 | ||
![]() |
790e540c19 | ||
![]() |
16074c565f | ||
![]() |
2a1dd55b11 | ||
![]() |
be20f760ab | ||
![]() |
8050394003 | ||
![]() |
ff8b5bc61b | ||
![]() |
ef8797821f | ||
![]() |
5f2797e7cc | ||
![]() |
e286702f4c | ||
![]() |
c58aaf545f | ||
![]() |
990f2dc1cf | ||
![]() |
774b4313f2 | ||
![]() |
1ecbc2ff0f | ||
![]() |
fd8e38f8cd | ||
![]() |
e86d4db55c | ||
![]() |
9420c74101 | ||
![]() |
b1bef9c21d | ||
![]() |
5b0ef7ea98 | ||
![]() |
ab53c414bc | ||
![]() |
d547ace749 | ||
![]() |
b47e0cffdd | ||
![]() |
3af35aee9e | ||
![]() |
02314ac7dd | ||
![]() |
e7c4e87ac4 | ||
![]() |
de58bfbb7f | ||
![]() |
0dda4c06b1 | ||
![]() |
79fd6143ec | ||
![]() |
8f89e3f7f4 | ||
![]() |
fc01d11b8d | ||
![]() |
0c28d8dcbe | ||
![]() |
764eaadd25 | ||
![]() |
273771ffec | ||
![]() |
32ce9ce919 | ||
![]() |
34a070f5a6 | ||
![]() |
ac4975cd7a | ||
![]() |
fbbbfb9668 | ||
![]() |
eb9f5339b6 | ||
![]() |
a9714e73c8 | ||
![]() |
a99bc91eb0 | ||
![]() |
071d3c71d8 | ||
![]() |
afbcac9fb1 | ||
![]() |
51e5b56b3a | ||
![]() |
bb07fd42ce | ||
![]() |
bab626c325 | ||
![]() |
2a9131498f | ||
![]() |
35a232105e | ||
![]() |
19dd1a25d7 | ||
![]() |
53396c0e50 | ||
![]() |
0b8208fe7f | ||
![]() |
3d276d50b4 | ||
![]() |
b1b731340e | ||
![]() |
b9b02b4ff2 | ||
![]() |
ab5d23da11 | ||
![]() |
0554fe3652 | ||
![]() |
b0282fe36f | ||
![]() |
69b45e693b | ||
![]() |
9e97acc28d | ||
![]() |
b1e446a931 | ||
![]() |
938319cd44 | ||
![]() |
fee29001fa | ||
![]() |
6d894a1806 | ||
![]() |
f1fc5d79ca | ||
![]() |
0fd2c74a66 | ||
![]() |
bb99cf37e3 | ||
![]() |
7c47fe746c | ||
![]() |
65a1c4a016 | ||
![]() |
46418d0f2d | ||
![]() |
ad585e179f | ||
![]() |
8348a1ec8f | ||
![]() |
c18e00daa4 | ||
![]() |
418ba96334 | ||
![]() |
a60e782959 | ||
![]() |
8bab5733d7 | ||
![]() |
e3270dfd68 | ||
![]() |
a14997ffb8 | ||
![]() |
dd94f97572 | ||
![]() |
7d502fb448 | ||
![]() |
3ac87bbcda | ||
![]() |
f64799622d | ||
![]() |
6f0ad2b6c5 | ||
![]() |
b5750afb24 | ||
![]() |
442dd5e955 | ||
![]() |
172c2ae1aa | ||
![]() |
4f0e0af319 | ||
![]() |
cb382b1e7d | ||
![]() |
871bf3b88f | ||
![]() |
08360e401d | ||
![]() |
b611b1824a | ||
![]() |
1473d8474f | ||
![]() |
0ecc3394c3 | ||
![]() |
725985379a | ||
![]() |
8849b9b62c | ||
![]() |
caa2611ad5 | ||
![]() |
f8ff597963 | ||
![]() |
ff6e434caf | ||
![]() |
95bb12880d | ||
![]() |
257196664a | ||
![]() |
643bf95366 | ||
![]() |
36a187da39 | ||
![]() |
fec80f2835 | ||
![]() |
4e47653cf6 | ||
![]() |
c13004f985 | ||
![]() |
baba41e304 | ||
![]() |
d87e09a8b4 | ||
![]() |
33146ac353 | ||
![]() |
bb20af8f20 | ||
![]() |
9355ec44e0 | ||
![]() |
c63bd323ce | ||
![]() |
55db7105c5 | ||
![]() |
1c079e554b | ||
![]() |
48afb68f3a | ||
![]() |
21f409d5e2 | ||
![]() |
521e573be9 | ||
![]() |
abf9ae2dd9 | ||
![]() |
9f013f7de4 | ||
![]() |
7fc04fd5cd | ||
![]() |
7901b04c78 | ||
![]() |
653eea5840 | ||
![]() |
5a4055fb08 | ||
![]() |
4d68a12f03 | ||
![]() |
0e951da64b | ||
![]() |
38dab040b3 | ||
![]() |
e9f6af61f9 | ||
![]() |
b06c4e2711 | ||
![]() |
1686f4e857 | ||
![]() |
9f57732af2 | ||
![]() |
329382c1da | ||
![]() |
fadc03df21 | ||
![]() |
54ee0e28ab | ||
![]() |
92fc53ebef | ||
![]() |
7e7a1613cf | ||
![]() |
f73c4643ef | ||
![]() |
8b94e8f260 | ||
![]() |
41bc17a27f |
12
.github/FUNDING.yml
vendored
@@ -1,12 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: MaxK
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
5
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -18,5 +18,10 @@ about: Create a bug report
|
||||
<!-- Paste the output of "mpd --version" here -->
|
||||
|
||||
|
||||
## Configuration
|
||||
<!-- Paste your MPD configuration here -->
|
||||
|
||||
|
||||
## Log
|
||||
<!-- Paste relevant portions of the log file here (--verbose) -->
|
||||
<!-- If MPD has crashed, include a backtrace (see https://mpd.readthedocs.io/en/stable/user.html#mpd-crashes) -->
|
||||
|
9
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,9 +0,0 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask a question about MPD
|
||||
---
|
||||
|
||||
<!-- Before you ask a question on GitHub, please read MPD's
|
||||
documentation. A copy is available at
|
||||
https://www.musicpd.org/doc/html/ -->
|
||||
## Question
|
6
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
335
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,335 @@
|
||||
---
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'AUTHORS'
|
||||
- 'COPYING'
|
||||
- 'LICENSES/**'
|
||||
- 'NEWS'
|
||||
- 'README.md'
|
||||
- 'android/**'
|
||||
- 'build/**'
|
||||
- 'doc/**'
|
||||
- 'python/**'
|
||||
- 'subprojects/**'
|
||||
- 'systemd/**'
|
||||
- 'win32/**'
|
||||
branches:
|
||||
- master
|
||||
- v0.23.x
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'AUTHORS'
|
||||
- 'COPYING'
|
||||
- 'LICENSES/**'
|
||||
- 'NEWS'
|
||||
- 'README.md'
|
||||
- 'android/**'
|
||||
- 'build/**'
|
||||
- 'doc/**'
|
||||
- 'python/**'
|
||||
- 'subprojects/**'
|
||||
- 'systemd/**'
|
||||
- 'win32/**'
|
||||
branches:
|
||||
- master
|
||||
- v0.23.x
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [gcc14, gcc12, clang]
|
||||
include:
|
||||
- compiler: gcc14
|
||||
os: ubuntu-24.04
|
||||
cc: gcc-14
|
||||
cxx: g++-14
|
||||
packages: g++-14
|
||||
meson_options:
|
||||
- compiler: gcc12
|
||||
os: ubuntu-24.04
|
||||
cc: gcc-12
|
||||
cxx: g++-12
|
||||
packages: g++-12
|
||||
meson_options:
|
||||
- compiler: clang
|
||||
os: ubuntu-24.04
|
||||
cc: clang
|
||||
cxx: clang++
|
||||
packages: clang
|
||||
meson_options: --force-fallback-for=fmt,gtest
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
env:
|
||||
CC: ccache ${{ matrix.cc }}
|
||||
CXX: ccache ${{ matrix.cxx }}
|
||||
|
||||
steps:
|
||||
- id: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
${{ matrix.packages }} \
|
||||
meson \
|
||||
ccache \
|
||||
libfmt-dev \
|
||||
libgtest-dev \
|
||||
libpcre2-dev \
|
||||
libsystemd-dev libdbus-1-dev \
|
||||
libicu-dev \
|
||||
libcurl4-gnutls-dev \
|
||||
libpcre2-dev \
|
||||
libavahi-client-dev \
|
||||
libmad0-dev libmpg123-dev libid3tag0-dev \
|
||||
libflac-dev libvorbis-dev libopus-dev libogg-dev \
|
||||
libadplug-dev libaudiofile-dev libsndfile1-dev libfaad-dev \
|
||||
libfluidsynth-dev libgme-dev libmikmod-dev libmodplug-dev \
|
||||
libmpcdec-dev libwavpack-dev libwildmidi-dev \
|
||||
libsidplay2-dev libsidutils-dev libresid-builder-dev \
|
||||
libavcodec-dev libavformat-dev \
|
||||
libmp3lame-dev libtwolame-dev libshine-dev \
|
||||
libsamplerate0-dev libsoxr-dev \
|
||||
libbz2-dev libcdio-paranoia-dev libiso9660-dev libmms-dev \
|
||||
libzzip-dev \
|
||||
libyajl-dev libexpat-dev \
|
||||
libasound2-dev libao-dev libjack-jackd2-dev libopenal-dev \
|
||||
libpulse-dev libshout3-dev \
|
||||
libsndio-dev \
|
||||
libmpdclient-dev \
|
||||
libnfs-dev \
|
||||
libupnp-dev \
|
||||
libsqlite3-dev \
|
||||
libchromaprint-dev \
|
||||
libgcrypt20-dev
|
||||
|
||||
- id: cache-ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: ${{ matrix.os }}-${{ matrix.compiler }}
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
meson setup \
|
||||
-Ddocumentation=disabled \
|
||||
-Dtest=true \
|
||||
-Dsystemd=enabled \
|
||||
-Dpcre=enabled \
|
||||
--wrap-mode nofallback \
|
||||
${{ matrix.meson_options }} \
|
||||
output/full
|
||||
|
||||
- name: Build
|
||||
run: meson compile -C output/full --verbose
|
||||
|
||||
- name: Unit Tests
|
||||
run: meson test -C output/full --print-errorlogs
|
||||
|
||||
- name: Configure Mini
|
||||
run: |
|
||||
meson setup \
|
||||
-Dbuildtype=minsize \
|
||||
-Dauto_features=disabled \
|
||||
-Dtest=true \
|
||||
-Ddaemon=false \
|
||||
-Dinotify=false -Depoll=false -Deventfd=false \
|
||||
-Dsignalfd=false \
|
||||
-Dtcp=false \
|
||||
-Ddsd=false \
|
||||
-Ddatabase=false \
|
||||
-Dneighbor=false \
|
||||
-Dcue=false \
|
||||
-Dfifo=false \
|
||||
-Dhttpd=false -Dpipe=false -Drecorder=false \
|
||||
-Dsnapcast=false \
|
||||
--wrap-mode nofallback \
|
||||
${{ matrix.meson_options }} \
|
||||
output/mini
|
||||
|
||||
- name: Build Mini
|
||||
run: meson compile -C output/mini --verbose
|
||||
|
||||
- name: Unit Tests Mini
|
||||
run: meson test -C output/mini --print-errorlogs
|
||||
|
||||
build-macos:
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [clang]
|
||||
include:
|
||||
- compiler: clang
|
||||
os: macos-latest
|
||||
cc: clang
|
||||
cxx: clang++
|
||||
brew_packages: >
|
||||
meson ninja
|
||||
pkgconf
|
||||
ccache
|
||||
expat
|
||||
fmt
|
||||
googletest
|
||||
pcre2
|
||||
icu4c
|
||||
ffmpeg
|
||||
libnfs
|
||||
libupnp
|
||||
libid3tag
|
||||
chromaprint
|
||||
libsamplerate
|
||||
libsoxr
|
||||
flac
|
||||
opus
|
||||
libvorbis
|
||||
faad2
|
||||
sqlite
|
||||
wavpack
|
||||
libmpdclient
|
||||
meson_options: --force-fallback-for=fmt,gtest
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
env:
|
||||
CC: ccache ${{ matrix.cc }}
|
||||
CXX: ccache ${{ matrix.cxx }}
|
||||
|
||||
steps:
|
||||
- id: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew update
|
||||
brew install \
|
||||
${{ matrix.brew_packages }}
|
||||
|
||||
- id: cache-ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: ${{ matrix.os }}-${{ matrix.compiler }}
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
meson setup \
|
||||
-Ddocumentation=disabled \
|
||||
-Dtest=true \
|
||||
-Dpcre=enabled \
|
||||
--wrap-mode nofallback \
|
||||
${{ matrix.meson_options }} \
|
||||
output/full
|
||||
|
||||
- name: Build
|
||||
run: meson compile -C output/full --verbose
|
||||
|
||||
- name: Unit Tests
|
||||
run: meson test -C output/full --print-errorlogs
|
||||
|
||||
- name: Configure Mini
|
||||
run: |
|
||||
meson setup \
|
||||
-Dbuildtype=minsize \
|
||||
-Dauto_features=disabled \
|
||||
-Dtest=true \
|
||||
-Ddaemon=false \
|
||||
-Dinotify=false -Depoll=false -Deventfd=false \
|
||||
-Dsignalfd=false \
|
||||
-Dtcp=false \
|
||||
-Ddsd=false \
|
||||
-Ddatabase=false \
|
||||
-Dneighbor=false \
|
||||
-Dcue=false \
|
||||
-Dfifo=false \
|
||||
-Dhttpd=false -Dpipe=false -Drecorder=false \
|
||||
-Dsnapcast=false \
|
||||
--wrap-mode nofallback \
|
||||
${{ matrix.meson_options }} \
|
||||
output/mini
|
||||
|
||||
- name: Build Mini
|
||||
run: meson compile -C output/mini --verbose
|
||||
|
||||
- name: Unit Tests Mini
|
||||
run: meson test -C output/mini --print-errorlogs
|
||||
|
||||
build-msys2:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: ['MINGW64', 'UCRT64']
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- id: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{matrix.platform}}
|
||||
pacboy: >-
|
||||
cc:p
|
||||
ccache:p
|
||||
cmake:p
|
||||
dbus:p
|
||||
faad2:p
|
||||
ffmpeg:p
|
||||
fmt:p
|
||||
flac:p
|
||||
gtest:p
|
||||
jack2:p
|
||||
libao:p
|
||||
libid3tag:p
|
||||
libmad:p
|
||||
libmpcdec:p
|
||||
libopenmpt:p
|
||||
libsamplerate:p
|
||||
libshout:p
|
||||
libsndfile:p
|
||||
libsoxr:p
|
||||
libvorbis:p
|
||||
meson:p
|
||||
ninja:p
|
||||
opus:p
|
||||
pulseaudio:p
|
||||
shine:p
|
||||
twolame:p
|
||||
yajl:p
|
||||
|
||||
- id: cache-ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: ${{ matrix.platform }}
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
meson setup \
|
||||
-Dbzip2=disabled \
|
||||
-Dcdio_paranoia=disabled \
|
||||
-Dchromaprint=disabled \
|
||||
-Ddocumentation=disabled \
|
||||
-Dfluidsynth=disabled \
|
||||
-Dicu=disabled \
|
||||
-Diso9660=disabled \
|
||||
-Dmikmod=disabled \
|
||||
-Dmpg123=disabled \
|
||||
-Dnfs=disabled \
|
||||
-Dsidplay=disabled \
|
||||
-Dudisks=disabled \
|
||||
-Dupnp=disabled \
|
||||
-Dwavpack=disabled \
|
||||
-Dzzip=disabled \
|
||||
-Dtest=true \
|
||||
"${{github.workspace}}/build"
|
||||
|
||||
- name: Build
|
||||
run: meson compile -C "${{github.workspace}}/build" --verbose
|
||||
|
||||
- name: Unit Tests
|
||||
run: meson test -C "${{github.workspace}}/build" --print-errorlogs
|
68
.github/workflows/build_android.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'AUTHORS'
|
||||
- 'COPYING'
|
||||
- 'LICENSES/**'
|
||||
- 'NEWS'
|
||||
- 'README.md'
|
||||
- 'build/**'
|
||||
- 'doc/**'
|
||||
- 'subprojects/**'
|
||||
- 'systemd/**'
|
||||
- 'win32/**'
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'AUTHORS'
|
||||
- 'COPYING'
|
||||
- 'LICENSES/**'
|
||||
- 'NEWS'
|
||||
- 'README.md'
|
||||
- 'build/**'
|
||||
- 'doc/**'
|
||||
- 'subprojects/**'
|
||||
- 'systemd/**'
|
||||
- 'win32/**'
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
build-android:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- id: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
meson \
|
||||
ccache \
|
||||
quilt
|
||||
|
||||
- id: cache-ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: android
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir -p output/android
|
||||
cd ./output/android
|
||||
../../android/build.py $ANDROID_SDK_ROOT $ANDROID_NDK_HOME arm64-v8a \
|
||||
--buildtype=debugoptimized -Db_ndebug=true \
|
||||
-Dwrap_mode=forcefallback
|
||||
|
||||
cd -
|
||||
cd ./android
|
||||
export JAVA_HOME=$JAVA_HOME_17_X64
|
||||
./gradlew assembleDebug
|
||||
|
8
.gitignore
vendored
@@ -1,11 +1,5 @@
|
||||
*~
|
||||
|
||||
.#*
|
||||
.stgit*
|
||||
|
||||
/output/
|
||||
|
||||
__pycache__/
|
||||
|
||||
/.clangd/
|
||||
/compile_commands.json
|
||||
result
|
||||
|
19
.readthedocs.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
# Read the Docs configuration file for Sphinx projects
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the OS, Python version and other tools you might need
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.12"
|
||||
|
||||
python:
|
||||
install:
|
||||
- requirements: "doc/requirements.txt"
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: doc/conf.py
|
113
.travis.yml
@@ -1,113 +0,0 @@
|
||||
language: cpp
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# Ubuntu Focal (20.04) with GCC 9.3
|
||||
- os: linux
|
||||
dist: focal
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- meson
|
||||
- libgtest-dev
|
||||
- libboost-dev
|
||||
|
||||
# Ubuntu Focal (20.04) with GCC 9.3 on big-endian
|
||||
- os: linux
|
||||
arch: s390x
|
||||
dist: focal
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- meson
|
||||
- libgtest-dev
|
||||
- libboost-dev
|
||||
|
||||
# Ubuntu Focal (20.04) with GCC 9.3 on ARM64
|
||||
- os: linux
|
||||
arch: arm64
|
||||
dist: focal
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- meson
|
||||
- libgtest-dev
|
||||
- libboost-dev
|
||||
|
||||
# Ubuntu Trusty (16.04) with GCC 8
|
||||
- os: linux
|
||||
dist: trusty
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:mhier/libboost-latest'
|
||||
- sourceline: 'ppa:ricotz/toolchain'
|
||||
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
|
||||
packages:
|
||||
- g++-8
|
||||
- libgtest-dev
|
||||
- boost1.67
|
||||
- python3.6
|
||||
- python3-urllib3
|
||||
- ninja-build
|
||||
before_install:
|
||||
- wget https://bootstrap.pypa.io/get-pip.py
|
||||
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
|
||||
install:
|
||||
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
|
||||
env:
|
||||
# use gold as workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17068
|
||||
- MATRIX_EVAL="export CC='ccache gcc-8' CXX='ccache g++-8' LDFLAGS=-fuse-ld=gold PATH=\$HOME/.local/bin:\$PATH"
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode11.6
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
- meson
|
||||
- googletest
|
||||
- icu4c
|
||||
- ffmpeg
|
||||
- libnfs
|
||||
- yajl
|
||||
- libupnp
|
||||
- libid3tag
|
||||
- chromaprint
|
||||
- libsamplerate
|
||||
- libsoxr
|
||||
# libzzip appears to be broken on Homebrew: "ld: library not found for -lzzip"
|
||||
#- libzzip
|
||||
- flac
|
||||
- opus
|
||||
- libvorbis
|
||||
- faad2
|
||||
- wavpack
|
||||
- libmpdclient
|
||||
env:
|
||||
- MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1"
|
||||
|
||||
cache:
|
||||
apt: true
|
||||
ccache: true
|
||||
directories:
|
||||
- $HOME/Library/Caches/Homebrew
|
||||
|
||||
before_cache:
|
||||
- test "$TRAVIS_OS_NAME" != "osx" || brew cleanup
|
||||
|
||||
before_install:
|
||||
- eval "${MATRIX_EVAL}"
|
||||
|
||||
install:
|
||||
|
||||
before_script:
|
||||
- ccache -s
|
||||
|
||||
script:
|
||||
- eval "${MATRIX_EVAL}"
|
||||
- OPTIONS="-Dtest=true"
|
||||
- meson . output --werror $OPTIONS
|
||||
- ninja -C output -v test
|
||||
- ccache -s
|
2
AUTHORS
@@ -1,5 +1,5 @@
|
||||
Music Player Daemon - http://www.musicpd.org
|
||||
Copyright 2003-2021 The Music Player Daemon Project
|
||||
Copyright 2003-2025 The Music Player Daemon Project
|
||||
|
||||
The following people have contributed code to MPD:
|
||||
|
||||
|
9
LICENSES/BSD-2-Clause.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Copyright (c) <year> <owner>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
117
LICENSES/GPL-2.0-or-later.txt
Normal file
@@ -0,0 +1,117 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification follow.
|
||||
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice
|
8
LICENSES/ISC.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
ISC License:
|
||||
|
||||
Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
|
||||
Copyright (c) 1995-2003 by Internet Software Consortium
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
175
LICENSES/LGPL-2.1-only.txt
Normal file
@@ -0,0 +1,175 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run.
|
||||
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
one line to give the library's name and an idea of what it does.
|
||||
Copyright (C) year name of author
|
||||
|
||||
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in
|
||||
the library `Frob' (a library for tweaking knobs) written
|
||||
by James Random Hacker.
|
||||
|
||||
signature of Ty Coon, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
That's all there is to it!
|
336
NEWS
@@ -1,3 +1,337 @@
|
||||
ver 0.24.1 (not yet released)
|
||||
* output
|
||||
- sndio: fix rounding error in volume calculation
|
||||
* log: include year in time stamp
|
||||
* macOS: implement standard directories
|
||||
* fix build failure in the "id3tag" Meson subproject
|
||||
* doc: use "sphinx_rtd_theme" only if it is installed
|
||||
|
||||
ver 0.24 (2025/03/11)
|
||||
* protocol
|
||||
- new command "searchcount" (case-insensitive "count")
|
||||
- "playlistfind"/"playlistsearch" have "sort" and "window" parameters
|
||||
- allow range in "playlistmove"
|
||||
- "save" can append to or replace an existing playlist
|
||||
- filter "prio" (for "playlistfind"/"playlistsearch")
|
||||
- limit "player" idle events to the current partition
|
||||
- operator "starts_with"
|
||||
- show PCRE support in "config" response
|
||||
- apply Unicode normalization to case-insensitive filter expressions
|
||||
- stickers on playlists and some tag types
|
||||
- new commands "stickernames", "stickertypes", "stickernamestypes", "playlistlength", "searchplaylist", "protocol"
|
||||
- new "search"/"find" filter "added-since"
|
||||
- allow range in listplaylist and listplaylistinfo
|
||||
- "sticker find" supports sort and window parameter and new sticker compare operators "eq", "lt", "gt", "contains" and "starts_with"
|
||||
- consume only idle flags that were subscribed to
|
||||
- volume command is no longer deprecated
|
||||
- new "available" and "reset" subcommands for tagtypes
|
||||
- searching stored playlists respond now with song position
|
||||
- new sticker subcommand "inc" and "dec"
|
||||
* database
|
||||
- attribute "added" shows when each song was added to the database
|
||||
- proxy: require MPD 0.21 or later
|
||||
- proxy: require libmpdclient 2.15 or later
|
||||
* archive
|
||||
- add option to disable archive plugins in mpd.conf
|
||||
* storage
|
||||
- curl: optimize database update
|
||||
- nfs: require libnfs 4.0 or later
|
||||
- nfs: support libnfs 6 (API version 2)
|
||||
- nfs: support libnfs URL arguments
|
||||
* input
|
||||
- alsa: limit ALSA buffer time to 2 seconds
|
||||
- alsa: set up a channel map
|
||||
- alsa: support the alsa-lib 1.2.11 API
|
||||
- alsa: add option "close_on_pause"
|
||||
- curl: add "connect_timeout" configuration
|
||||
* decoder
|
||||
- ffmpeg: require FFmpeg 4.0 or later
|
||||
- ffmpeg: query supported demuxers at runtime
|
||||
- hybrid_dsd: remove
|
||||
- mpg123: prefer over "mad"
|
||||
- mpg123: support streaming
|
||||
- opus: implement bitrate calculation
|
||||
- sidplay: require libsidplayfp (drop support for the original sidplay)
|
||||
- wavpack: require libwavpack version 5
|
||||
- fix MixRamp bug
|
||||
* resampler
|
||||
- soxr: require libsoxr 0.1.2 or later
|
||||
* player
|
||||
- add option "mixramp_analyzer" to scan MixRamp tags on-the-fly
|
||||
- "one-shot" consume mode
|
||||
* tags
|
||||
- new tags "TitleSort", "Mood", "ShowMovement"
|
||||
* output
|
||||
- add option "always_off"
|
||||
- alsa: require alsa-lib 1.1 or later
|
||||
- pipewire: map tags "Date" and "Comment"
|
||||
* switch to C++20
|
||||
- GCC 12 or clang 14 (or newer) recommended
|
||||
* static partition configuration
|
||||
* Windows
|
||||
- build with libsamplerate
|
||||
- remove JACK DLL support
|
||||
* remove Haiku support
|
||||
* remove Boost dependency
|
||||
* require libfmt 9 or later
|
||||
* documentation: switch to sphinx-rtd-theme
|
||||
* require Meson 1.0
|
||||
|
||||
ver 0.23.17 (2025/01/29)
|
||||
* protocol
|
||||
- "albumart" tries to send larger chunks if available
|
||||
- explicitly disallow "idle" and "noidle" in command lists
|
||||
* storage
|
||||
- nfs: require libnfs 4.0 or later
|
||||
* database
|
||||
- inotify: trigger update after symlink was created
|
||||
* decoder
|
||||
- ffmpeg: prefer over sndfile and audiofile for its DTS-WAV support
|
||||
* support libfmt 11.1
|
||||
|
||||
ver 0.23.16 (2024/12/03)
|
||||
* database
|
||||
- fix integer overflows with 64-bit inode numbers
|
||||
* filter
|
||||
- ffmpeg: fix for filters producing no output
|
||||
* support libfmt 11
|
||||
* support ICU 76
|
||||
|
||||
ver 0.23.15 (2023/12/20)
|
||||
* decoder
|
||||
- ffmpeg: fix build failure with FFmpeg 6.1
|
||||
* output
|
||||
- alsa: limit buffer time to 2 seconds
|
||||
|
||||
ver 0.23.14 (2023/10/08)
|
||||
* decoder
|
||||
- flac: fix scanning files with non-ASCII names on Windows
|
||||
- mad: fix calculation of LAME peak values
|
||||
* mixer
|
||||
- wasapi: fix problem setting volume
|
||||
* more libfmt 10 fixes
|
||||
* fix auto-detected systemd unit directory
|
||||
* Android
|
||||
- require Android 7 or newer
|
||||
|
||||
ver 0.23.13 (2023/05/22)
|
||||
* input
|
||||
- curl: fix busy loop after connection failed
|
||||
- curl: hide "404" log messages for non-existent ".mpdignore" files
|
||||
* archive
|
||||
- zzip: fix crash bug
|
||||
* database
|
||||
- simple: reveal hidden songs after deleting containing CUE
|
||||
* decoder
|
||||
- ffmpeg: reorder to a lower priority than "gme"
|
||||
- gme: require GME 0.6 or later
|
||||
* output
|
||||
- pipewire: fix corruption bug due to missing lock
|
||||
* Linux
|
||||
- shut down if parent process dies in --no-daemon mode
|
||||
- determine systemd unit directories via pkg-config
|
||||
* support libfmt 10
|
||||
|
||||
ver 0.23.12 (2023/01/17)
|
||||
* input
|
||||
- curl: require CURL 7.55.0 or later
|
||||
* decoder
|
||||
- mad: fix integer underflow with very small files
|
||||
* tags
|
||||
- fix crash bug due to race condition
|
||||
* output
|
||||
- pipewire: adjust to PipeWire 0.3.64 API change
|
||||
* fix build failures with GCC 13
|
||||
|
||||
ver 0.23.11 (2022/11/28)
|
||||
* database
|
||||
- simple: move default database to ~/.cache/mpd/db from ~/.cache/mpd.db
|
||||
- simple: default "cache_directory" to ~/.cache/mpd/mounts
|
||||
* macOS: fix build failure "no archive members specified"
|
||||
* Windows
|
||||
- fix crash bug (stack buffer overflow) after I/O errors
|
||||
- fix path traversal bug because backslash was allowed in playlist names
|
||||
* Android/Windows
|
||||
- update OpenSSL to 3.0.7
|
||||
- re-enable CURL's verbose error strings
|
||||
|
||||
ver 0.23.10 (2022/10/14)
|
||||
* storage
|
||||
- curl: fix file time stamps
|
||||
* decoder
|
||||
- ffmpeg: fix libfmt 9 compiler warning
|
||||
* encoder
|
||||
- flac: fix failure when libFLAC is built without Ogg support
|
||||
* output
|
||||
- alsa: fix crash bug
|
||||
* Windows
|
||||
- log to stdout by default, don't require "log_file" setting
|
||||
|
||||
ver 0.23.9 (2022/08/18)
|
||||
* input
|
||||
- cdio_paranoia: add options "mode" and "skip"
|
||||
* decoder
|
||||
- ffmpeg: support FFmpeg 5.1
|
||||
* filter
|
||||
- replay gain: fix delayed volume display with handler=mixer
|
||||
* output
|
||||
- pipewire: set app icon
|
||||
* fix bogus volume levels with multiple partitions
|
||||
* improve iconv detection
|
||||
* macOS: fix macOS 10 build problem (0.23.8 regression)
|
||||
* Android
|
||||
- load mpd.conf from app data directory
|
||||
|
||||
ver 0.23.8 (2022/07/09)
|
||||
* storage
|
||||
- curl: fix crash if web server does not understand WebDAV
|
||||
* input
|
||||
- cdio_paranoia: fix crash if no drive was found
|
||||
- cdio_paranoia: faster cancellation
|
||||
- cdio_paranoia: don't scan for replay gain tags
|
||||
- pipewire: fix playback of very short tracks
|
||||
- pipewire: drop all buffers before manual song change
|
||||
- pipewire: fix stuttering after manual song change
|
||||
- snapcast: fix busy loop while paused
|
||||
- snapcast: fix stuttering after resuming playback
|
||||
* mixer
|
||||
- better error messages
|
||||
- alsa: fix setting volume before playback starts
|
||||
- pipewire: fix crash bug
|
||||
- pipewire: fix volume change events with PipeWire 0.3.53
|
||||
- pipewire: don't force initial volume=100%
|
||||
* support libfmt 9
|
||||
|
||||
ver 0.23.7 (2022/05/09)
|
||||
* database
|
||||
- upnp: support pupnp 1.14
|
||||
* decoder
|
||||
- ffmpeg: fix HLS seeking
|
||||
- opus: fix missing song length on high-latency files
|
||||
* output
|
||||
- shout: require at least libshout 2.4.0
|
||||
* mixer
|
||||
- pipewire: fix volume restore
|
||||
- software: update volume of disabled outputs
|
||||
* support libiconv
|
||||
|
||||
ver 0.23.6 (2022/03/14)
|
||||
* protocol
|
||||
- support filename "cover.webp" for "albumart" command
|
||||
- support "readcomments" and "readpicture" on CUE tracks
|
||||
* decoder
|
||||
- ffmpeg: fix end-of-file check (update stuck at empty files)
|
||||
- opus: fix "readpicture" on Opus files
|
||||
* output
|
||||
- pipewire: fix crash bug if setting volume before playback starts
|
||||
- wasapi: fix resume after pause
|
||||
|
||||
ver 0.23.5 (2021/12/01)
|
||||
* protocol
|
||||
- support relative offsets for "searchadd"
|
||||
- fix "searchaddpl" bug (bogus error "Bad position")
|
||||
* database
|
||||
- upnp: fix crash bug
|
||||
* tags
|
||||
- fix MixRamp support
|
||||
* migrate to PCRE2
|
||||
* GCC 12 build fixes
|
||||
|
||||
ver 0.23.4 (2021/11/11)
|
||||
* protocol
|
||||
- add optional position parameter to "searchaddpl"
|
||||
* decoder
|
||||
- ffmpeg: support libavcodec 59
|
||||
* output
|
||||
- alsa: add option "thesycon_dsd_workaround" to work around device bug
|
||||
* fix crash on debug builds if startup fails
|
||||
* systemd
|
||||
- remove "RuntimeDirectory" directive because it caused problems
|
||||
- ignore the "pid_file" setting if started as systemd service
|
||||
* Windows
|
||||
- enable the "openmpt" decoder plugin
|
||||
|
||||
ver 0.23.3 (2021/10/31)
|
||||
* protocol
|
||||
- add optional position parameter to "add" and "playlistadd"
|
||||
- allow range in "playlistdelete"
|
||||
* database
|
||||
- fix scanning files with question mark in the name
|
||||
- inotify: fix use-after-free bug
|
||||
* output
|
||||
- alsa: add option "stop_dsd_silence" to work around DSD DAC noise
|
||||
* macOS: fix libfmt related build failure
|
||||
* systemd: add "RuntimeDirectory" directive
|
||||
|
||||
ver 0.23.2 (2021/10/22)
|
||||
* protocol
|
||||
- fix "albumart" timeout bug
|
||||
* input
|
||||
- nfs: fix playback bug
|
||||
* output
|
||||
- pipewire: send artist and title to PipeWire
|
||||
- pipewire: DSD support
|
||||
* neighbor
|
||||
- mention failed plugin name in error message
|
||||
* player
|
||||
- fix cross-fade regression
|
||||
* fix crash with libfmt versions older than 7
|
||||
|
||||
ver 0.23.1 (2021/10/19)
|
||||
* protocol
|
||||
- use decimal notation instead of scientific notation
|
||||
- "load" supports relative positions
|
||||
* output
|
||||
- emit "mixer" idle event when replay gain changes volume
|
||||
- pipewire: emit "mixer" idle events on external volume change
|
||||
- pipewire: attempt to change the graph sample rate
|
||||
- snapcast: fix time stamp bug which caused "Failed to get chunk"
|
||||
* fix libfmt linker problems
|
||||
* fix broken password authentication
|
||||
|
||||
ver 0.23 (2021/10/14)
|
||||
* protocol
|
||||
- new command "getvol"
|
||||
- show the audio format in "playlistinfo"
|
||||
- support "listfiles" with arbitrary storage plugins
|
||||
- support relative positions in "addid"
|
||||
- fix relative positions in "move" and "moveid"
|
||||
- add "position" parameter to "findadd" and "searchadd"
|
||||
- add position parameter to "load"
|
||||
* database
|
||||
- proxy: require MPD 0.20 or later
|
||||
- proxy: require libmpdclient 2.11 or later
|
||||
- proxy: split search into chunks to avoid exceeding the output buffer
|
||||
- simple: add option to hide CUE target songs
|
||||
- upnp: support libnpupnp instead of libupnp
|
||||
* archive
|
||||
- zzip, iso9660: ignore file names which are invalid UTF-8
|
||||
* decoder
|
||||
- openmpt: new plugin
|
||||
- wavpack: fix WVC file support
|
||||
* player
|
||||
- do not cross-fade songs shorter than 20 seconds
|
||||
* output
|
||||
- oss: support DSD over PCM
|
||||
- pipewire: new plugin
|
||||
- snapcast: new plugin
|
||||
* tags
|
||||
- new tags "ComposerSort", "Ensemble", "Movement", "MovementNumber", and "Location"
|
||||
* split permission "player" from "control"
|
||||
* add option "host_permissions"
|
||||
* new build-time dependency: libfmt
|
||||
|
||||
ver 0.22.11 (2021/08/24)
|
||||
* protocol
|
||||
- fix "albumart" crash
|
||||
* filter
|
||||
- ffmpeg: pass "channel_layout" instead of "channels" to buffersrc
|
||||
- ffmpeg: fix "av_buffersink_get_frame() failed: Resource temporarily unavailable"
|
||||
- ffmpeg: support double-precision samples (by converting to single precision)
|
||||
* Android
|
||||
- build with NDK r23
|
||||
- playlist_directory defaults to "/sdcard/Android/data/org.musicpd/files/playlists"
|
||||
|
||||
ver 0.22.10 (2021/08/06)
|
||||
* protocol
|
||||
- support "albumart" for virtual tracks in CUE sheets
|
||||
@@ -715,7 +1049,7 @@ ver 0.20.10 (2017/08/24)
|
||||
* decoder
|
||||
- ffmpeg: support MusicBrainz ID3v2 tags
|
||||
* tags
|
||||
- aiff: fix FORM chunk size endianess (is big-endian)
|
||||
- aiff: fix FORM chunk size endianness (is big-endian)
|
||||
* mixer
|
||||
- osx: add a mixer for OSX.
|
||||
* fix crash when resuming playback before decoder is ready
|
||||
|
@@ -12,15 +12,15 @@ For basic installation instructions
|
||||
|
||||
# Users
|
||||
|
||||
- [Manual](http://www.musicpd.org/doc/user/)
|
||||
- [Forum](http://forum.musicpd.org/)
|
||||
- [Manual](https://mpd.readthedocs.io/en/stable/user.html)
|
||||
- [Forum](https://github.com/MusicPlayerDaemon/MPD/discussions)
|
||||
- [IRC](ircs://irc.libera.chat:6697/#mpd)
|
||||
- [Bug tracker](https://github.com/MusicPlayerDaemon/MPD/issues/)
|
||||
|
||||
# Developers
|
||||
|
||||
- [Protocol specification](http://www.musicpd.org/doc/protocol/)
|
||||
- [Developer manual](http://www.musicpd.org/doc/developer/)
|
||||
- [Protocol specification](https://mpd.readthedocs.io/en/latest/protocol.html)
|
||||
- [Developer manual](https://mpd.readthedocs.io/en/latest/developer.html)
|
||||
|
||||
# Legal
|
||||
|
||||
|
24
android/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
## ignoring .idea completely
|
||||
# until a good reason emerges not to
|
||||
/.idea
|
||||
##--
|
||||
## moved the following into .idea/.gitignore
|
||||
#/.idea/caches
|
||||
#/.idea/libraries
|
||||
#/.idea/modules.xml
|
||||
#/.idea/workspace.xml
|
||||
#/.idea/navEditor.xml
|
||||
#/.idea/assetWizardSettings.xml
|
||||
## --
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
# both were used: different spelling
|
||||
app/src/main/jnilibs/
|
||||
app/src/main/jniLibs/
|
@@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="58"
|
||||
android:versionName="0.22.10">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
|
||||
|
||||
<uses-feature android:name="android.software.leanback"
|
||||
android:required="false" />
|
||||
<uses-feature android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application android:allowBackup="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:icon="@drawable/icon"
|
||||
android:banner="@drawable/icon"
|
||||
android:label="@string/app_name">
|
||||
<activity android:name=".Settings"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".Settings"
|
||||
android:label="@string/app_name" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<receiver android:name=".Receiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<service android:name=".Main" android:process=":main"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
8
android/PrivacyPolicy.rst
Normal file
@@ -0,0 +1,8 @@
|
||||
MPD for Android Privacy Policy
|
||||
==============================
|
||||
|
||||
Music Player Daemon is an open source project currently maintained by
|
||||
`Max Kellermann <mailto:max.kellermann+googleplay@gmail.com>`__.
|
||||
|
||||
Music Player Daemon does not access, collect, use or share any
|
||||
personal or sensitive user data.
|
49
android/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Android
|
||||
|
||||
Notes and resources for MPD android maintainers.
|
||||
|
||||
## Build
|
||||
|
||||
See [Compiling for Android](https://github.com/MusicPlayerDaemon/MPD/blob/45cb098cd765af12316f8dca5635ef10a852e013/doc/user.rst#compiling-for-android)
|
||||
|
||||
## Android studio
|
||||
|
||||
### Version control
|
||||
|
||||
git ignoring .idea directory completely until a good reason emerges not to
|
||||
|
||||
* [How to manage projects under Version Control Systems (jetbrains.com)](https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems)
|
||||
|
||||
* [gradle.xml should work like workspace.xml? (jetbrains.com)](https://youtrack.jetbrains.com/issue/IDEA-55923)
|
||||
|
||||
### Native libraries
|
||||
|
||||
* [Include prebuilt native libraries (developer.android.com)](https://developer.android.com/studio/projects/gradle-external-native-builds#jniLibs)
|
||||
|
||||
## Permissions
|
||||
|
||||
### Files access
|
||||
|
||||
The required permission depends on android SDK version:
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
|
||||
Manifest.permission.READ_MEDIA_AUDIO
|
||||
else
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
|
||||
### Permission request
|
||||
|
||||
[Request runtime permissions](https://developer.android.com/training/permissions/requesting)
|
||||
|
||||
Since Android 6.0 (API level 23):
|
||||
|
||||
Android will ignore permission request and will not show the request dialog
|
||||
if the user's action implies "don't ask again."
|
||||
This leaves the app in a crippled state and the user confused.
|
||||
Google says "don't try to convince the user", so it returns false for `shouldShowRequestPermissionRationale`.
|
||||
|
||||
To help the user proceed, we show the `Request permission` button only if `shouldShowRequestPermissionRationale == true`
|
||||
because there's a good chance the permission request dialog will not be ignored.
|
||||
|
||||
If `shouldShowRequestPermissionRationale == false` we instead show the "rationale" message and a button to open
|
||||
the app info dialog where the user can explicitly grand the permission.
|
@@ -1,21 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
S=`dirname "$0"`
|
||||
ANDROID_ABI=$1
|
||||
STRIP=$2
|
||||
ZIP=$3
|
||||
UNSIGNED_APK=$4
|
||||
LIBMPD_SO=$5
|
||||
CLASSES_DEX=$6
|
||||
RESOURCES_APK=$7
|
||||
D=`dirname "$UNSIGNED_APK"`
|
||||
|
||||
rm -rf "$D/apk"
|
||||
mkdir -p "$D/apk/lib/$ANDROID_ABI"
|
||||
|
||||
"$STRIP" "$LIBMPD_SO" -o "$D/apk/lib/$ANDROID_ABI/`basename $LIBMPD_SO`"
|
||||
cp "$CLASSES_DEX" "$D/apk/"
|
||||
cp "$RESOURCES_APK" "$UNSIGNED_APK"
|
||||
|
||||
cd "$D/apk"
|
||||
exec zip -q -r -X "../`basename $UNSIGNED_APK`" .
|
@@ -1,59 +0,0 @@
|
||||
unsigned_apk = custom_target(
|
||||
'mpd-unsigned.apk',
|
||||
output: 'mpd-unsigned.apk',
|
||||
input: [mpd, classes_dex, resources_apk[0]],
|
||||
command: [
|
||||
join_paths(meson.current_source_dir(), 'make-unsigned-apk.sh'),
|
||||
android_abi,
|
||||
get_option('android_strip'),
|
||||
zip,
|
||||
'@OUTPUT0@',
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
||||
|
||||
if get_option('android_debug_keystore') != ''
|
||||
debug_apk = custom_target(
|
||||
'mpd-debug.apk',
|
||||
output: 'mpd-debug.apk',
|
||||
input: unsigned_apk,
|
||||
command: [
|
||||
jarsigner,
|
||||
'-keystore', get_option('android_debug_keystore'),
|
||||
'-storepass', 'android',
|
||||
'-signedjar', '@OUTPUT@',
|
||||
'@INPUT@',
|
||||
'androiddebugkey',
|
||||
],
|
||||
build_by_default: true
|
||||
)
|
||||
endif
|
||||
|
||||
if get_option('android_keystore') != '' and get_option('android_keyalias') != '' and get_option('android_keypass') != ''
|
||||
unaligned_apk = custom_target(
|
||||
'mpd-unaligned.apk',
|
||||
output: 'mpd-unaligned.apk',
|
||||
input: unsigned_apk,
|
||||
command: [
|
||||
jarsigner,
|
||||
'-digestalg', 'SHA1', '-sigalg', 'MD5withRSA',
|
||||
'-keystore', get_option('android_keystore'),
|
||||
'-storepass', get_option('android_keypass'),
|
||||
'-signedjar', '@OUTPUT@',
|
||||
'@INPUT@',
|
||||
get_option('android_keyalias'),
|
||||
],
|
||||
)
|
||||
|
||||
apk = custom_target(
|
||||
'mpd.apk',
|
||||
output: 'mpd.apk',
|
||||
input: unaligned_apk,
|
||||
command: [
|
||||
android_zipalign,
|
||||
'-f', '4',
|
||||
'@INPUT@', '@OUTPUT@',
|
||||
],
|
||||
build_by_default: true
|
||||
)
|
||||
endif
|
1
android/app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
128
android/app/build.gradle.kts
Normal file
@@ -0,0 +1,128 @@
|
||||
plugins {
|
||||
id("com.google.devtools.ksp")
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.jetbrains.kotlin.android)
|
||||
alias(libs.plugins.dagger.hilt.android)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "org.musicpd"
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "org.musicpd"
|
||||
minSdk = 24
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
aidl = true
|
||||
compose = true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.10"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
isMinifyEnabled = false
|
||||
}
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
// flavors
|
||||
flavorDimensions += "base"
|
||||
productFlavors {
|
||||
create("fail-test") {
|
||||
// To test System.loadLibrary("mpd") failure
|
||||
// exclude the native lib from the package
|
||||
packaging {
|
||||
jniLibs {
|
||||
// it appears the 'excludes' is applied to all flavors
|
||||
// even if it's only inside this flavor.
|
||||
// this filters by task name to apply the exclusion only
|
||||
// for this flavor name.
|
||||
// (clearing the 'abiFilters' will only create a universal apk
|
||||
// with all of the abi versions)
|
||||
gradle.startParameter.getTaskNames().forEach { task ->
|
||||
if (task.contains("fail-test", ignoreCase = true)) {
|
||||
println("NOTICE: excluding libmpd.so from package $task for testing")
|
||||
excludes += "**/libmpd.so"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
create("arm64-v8a") {
|
||||
ndk {
|
||||
// ABI to include in package
|
||||
//noinspection ChromeOsAbiSupport
|
||||
abiFilters += listOf("arm64-v8a")
|
||||
}
|
||||
}
|
||||
create("x86_64") {
|
||||
ndk {
|
||||
// ABI to include in package
|
||||
abiFilters += listOf("x86_64")
|
||||
}
|
||||
}
|
||||
create("universal") {
|
||||
ndk {
|
||||
// ABI to include in package
|
||||
abiFilters += listOf("arm64-v8a", "x86_64")
|
||||
}
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_9
|
||||
targetCompatibility = JavaVersion.VERSION_1_9
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_9.toString()
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.material.icons.extended)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
|
||||
implementation(libs.compose.settings.ui.m3)
|
||||
implementation(libs.compose.settings.storage.preferences)
|
||||
implementation(libs.accompanist.permissions)
|
||||
|
||||
implementation(libs.hilt.android)
|
||||
ksp(libs.dagger.compiler)
|
||||
ksp(libs.hilt.compiler)
|
||||
|
||||
implementation(libs.androidx.media3.session)
|
||||
|
||||
// Android Studio Preview support
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
|
||||
implementation(libs.androidx.appcompat)
|
||||
}
|
75
android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="73"
|
||||
android:versionName="0.23.15">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.software.leanback"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:banner="@mipmap/ic_banner"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/Theme.MPD"
|
||||
android:name=".MPDApplication">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<receiver
|
||||
android:name=".Receiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".AutomationReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="org.musicpd.action.StartService" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="org.musicpd.action.StopService" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name=".Main"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
@@ -5,6 +5,7 @@ interface IMain
|
||||
{
|
||||
void start();
|
||||
void stop();
|
||||
void setPauseOnHeadphonesDisconnect(boolean enabled);
|
||||
void setWakelockEnabled(boolean enabled);
|
||||
boolean isRunning();
|
||||
void registerCallback(IMainCallback cb);
|
@@ -5,5 +5,4 @@ interface IMainCallback
|
||||
void onStarted();
|
||||
void onStopped();
|
||||
void onError(String error);
|
||||
void onLog(int priority, String msg);
|
||||
}
|
BIN
android/app/src/main/ic_launcher-playstore.png
Normal file
After Width: | Height: | Size: 49 KiB |
24
android/app/src/main/java/org/musicpd/AutomationReceiver.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package org.musicpd
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
||||
class AutomationReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
|
||||
when(intent.action) {
|
||||
"org.musicpd.action.StartService" -> {
|
||||
val wakelock = Preferences.getBoolean(
|
||||
context,
|
||||
Preferences.KEY_WAKELOCK, false
|
||||
)
|
||||
Main.startService(context, wakelock)
|
||||
}
|
||||
"org.musicpd.action.StopService" -> {
|
||||
context.startService(Intent(context, Main::class.java)
|
||||
.setAction(Main.SHUTDOWN_ACTION))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
android/app/src/main/java/org/musicpd/Bridge.java
Normal file
@@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright The Music Player Daemon Project
|
||||
|
||||
package org.musicpd;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Bridge to native code.
|
||||
*/
|
||||
public class Bridge {
|
||||
|
||||
/* used by jni */
|
||||
public interface LogListener {
|
||||
public void onLog(int priority, String msg);
|
||||
}
|
||||
|
||||
public static native void run(Context context, LogListener logListener);
|
||||
public static native void shutdown();
|
||||
public static native void pause();
|
||||
public static native void playPause();
|
||||
public static native void playNext();
|
||||
public static native void playPrevious();
|
||||
}
|
45
android/app/src/main/java/org/musicpd/Loader.kt
Normal file
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright The Music Player Daemon Project
|
||||
package org.musicpd
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
|
||||
object Loader {
|
||||
private const val TAG = "Loader"
|
||||
|
||||
private var loaded: Boolean = false
|
||||
private var error: String? = null
|
||||
private val failReason: String get() = error ?: ""
|
||||
|
||||
val isLoaded: Boolean get() = loaded
|
||||
|
||||
init {
|
||||
load()
|
||||
}
|
||||
|
||||
private fun load() {
|
||||
if (loaded) return
|
||||
loaded = try {
|
||||
error = null
|
||||
System.loadLibrary("mpd")
|
||||
Log.i(TAG, "mpd lib loaded")
|
||||
true
|
||||
} catch (e: Throwable) {
|
||||
error = e.message ?: e.javaClass.simpleName
|
||||
Log.e(TAG, "failed to load mpd lib: $failReason")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun loadFailureMessage(context: Context): String {
|
||||
return context.getString(
|
||||
R.string.mpd_load_failure_message,
|
||||
Build.SUPPORTED_ABIS.joinToString(),
|
||||
Build.PRODUCT,
|
||||
Build.FINGERPRINT,
|
||||
failReason
|
||||
)
|
||||
}
|
||||
}
|
9
android/app/src/main/java/org/musicpd/MPDApplication.kt
Normal file
@@ -0,0 +1,9 @@
|
||||
package org.musicpd
|
||||
|
||||
import android.app.Application
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
||||
@HiltAndroidApp
|
||||
class MPDApplication : Application() {
|
||||
|
||||
}
|
76
android/app/src/main/java/org/musicpd/MPDPlayer.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package org.musicpd;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.common.SimpleBasePlayer;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@UnstableApi
|
||||
public class MPDPlayer extends SimpleBasePlayer {
|
||||
|
||||
List<MediaItemData> placeholderItems;
|
||||
public MPDPlayer(Looper looper) {
|
||||
super(looper);
|
||||
|
||||
// Dummy items to let us receive next and previous commands
|
||||
MediaItemData item0 = new MediaItemData.Builder(0)
|
||||
.build();
|
||||
MediaItemData item1 = new MediaItemData.Builder(1)
|
||||
.build();
|
||||
MediaItemData item2 = new MediaItemData.Builder(2)
|
||||
.build();
|
||||
MediaItemData[] items = new MediaItemData[] { item0, item1, item2 };
|
||||
|
||||
placeholderItems = Arrays.asList(items);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected State getState() {
|
||||
Commands commands = new Commands.Builder().addAll(
|
||||
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM,
|
||||
COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
|
||||
COMMAND_PLAY_PAUSE
|
||||
).build();
|
||||
|
||||
return new State.Builder()
|
||||
.setAvailableCommands(commands)
|
||||
.setPlaybackState(Player.STATE_READY)
|
||||
.setPlaylist(placeholderItems)
|
||||
.setCurrentMediaItemIndex(1)
|
||||
.build();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) {
|
||||
Bridge.playPause();
|
||||
return Futures.immediateVoidFuture();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@SuppressLint("SwitchIntDef")
|
||||
@Override
|
||||
protected ListenableFuture<?> handleSeek(int mediaItemIndex, long positionMs, int seekCommand) {
|
||||
switch (seekCommand) {
|
||||
case COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM:
|
||||
case COMMAND_SEEK_TO_PREVIOUS:
|
||||
Bridge.playPrevious();
|
||||
break;
|
||||
case COMMAND_SEEK_TO_NEXT_MEDIA_ITEM:
|
||||
case COMMAND_SEEK_TO_NEXT:
|
||||
Bridge.playNext();
|
||||
break;
|
||||
}
|
||||
return Futures.immediateVoidFuture();
|
||||
}
|
||||
}
|
326
android/app/src/main/java/org/musicpd/Main.kt
Normal file
@@ -0,0 +1,326 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright The Music Player Daemon Project
|
||||
package org.musicpd
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.media.AudioManager
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.Looper
|
||||
import android.os.PowerManager
|
||||
import android.os.PowerManager.WakeLock
|
||||
import android.os.RemoteCallbackList
|
||||
import android.os.RemoteException
|
||||
import android.util.Log
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.core.app.ServiceCompat
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.session.MediaSession
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.musicpd.Bridge.LogListener
|
||||
import org.musicpd.data.LoggingRepository
|
||||
import java.lang.reflect.Constructor
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class Main : Service(), Runnable {
|
||||
companion object {
|
||||
private const val TAG = "Main"
|
||||
private const val WAKELOCK_TAG = "mpd:wakelockmain"
|
||||
|
||||
private const val MAIN_STATUS_ERROR = -1
|
||||
private const val MAIN_STATUS_STOPPED = 0
|
||||
private const val MAIN_STATUS_STARTED = 1
|
||||
|
||||
private const val MSG_SEND_STATUS = 0
|
||||
|
||||
const val SHUTDOWN_ACTION: String = "org.musicpd.action.ShutdownMPD"
|
||||
|
||||
/*
|
||||
* start Main service without any callback
|
||||
*/
|
||||
@JvmStatic
|
||||
fun startService(context: Context, wakelock: Boolean) {
|
||||
val intent = Intent(context, Main::class.java)
|
||||
.putExtra("wakelock", wakelock)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) /* in Android 8+, we need to use this method
|
||||
or else we'll get "IllegalStateException:
|
||||
app is in background" */
|
||||
context.startForegroundService(intent)
|
||||
else context.startService(intent)
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var mpdApp: MPDApplication
|
||||
private lateinit var mpdLoader: Loader
|
||||
|
||||
private var mThread: Thread? = null
|
||||
private var mStatus = MAIN_STATUS_STOPPED
|
||||
private var mAbort = false
|
||||
private var mError: String? = null
|
||||
private val mCallbacks = RemoteCallbackList<IMainCallback>()
|
||||
private val mBinder: IBinder = MainStub(this)
|
||||
private var mPauseOnHeadphonesDisconnect = false
|
||||
private var mWakelock: WakeLock? = null
|
||||
|
||||
private var mMediaSession: MediaSession? = null
|
||||
|
||||
@JvmField
|
||||
@Inject
|
||||
var logging: LoggingRepository? = null
|
||||
|
||||
internal class MainStub(private val mService: Main) : IMain.Stub() {
|
||||
override fun start() {
|
||||
mService.start()
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
mService.stop()
|
||||
}
|
||||
|
||||
override fun setPauseOnHeadphonesDisconnect(enabled: Boolean) {
|
||||
mService.setPauseOnHeadphonesDisconnect(enabled)
|
||||
}
|
||||
|
||||
override fun setWakelockEnabled(enabled: Boolean) {
|
||||
mService.setWakelockEnabled(enabled)
|
||||
}
|
||||
|
||||
override fun isRunning(): Boolean {
|
||||
return mService.isRunning
|
||||
}
|
||||
|
||||
override fun registerCallback(cb: IMainCallback) {
|
||||
mService.registerCallback(cb)
|
||||
}
|
||||
|
||||
override fun unregisterCallback(cb: IMainCallback) {
|
||||
mService.unregisterCallback(cb)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
mpdLoader = Loader
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun sendMessage(
|
||||
@Suppress("SameParameterValue") what: Int,
|
||||
arg1: Int,
|
||||
arg2: Int,
|
||||
obj: Any?
|
||||
) {
|
||||
var i = mCallbacks.beginBroadcast()
|
||||
while (i > 0) {
|
||||
i--
|
||||
val cb = mCallbacks.getBroadcastItem(i)
|
||||
try {
|
||||
when (what) {
|
||||
MSG_SEND_STATUS -> when (arg1) {
|
||||
MAIN_STATUS_ERROR -> cb.onError(obj as String?)
|
||||
MAIN_STATUS_STOPPED -> cb.onStopped()
|
||||
MAIN_STATUS_STARTED -> cb.onStarted()
|
||||
}
|
||||
}
|
||||
} catch (ignored: RemoteException) {
|
||||
}
|
||||
}
|
||||
mCallbacks.finishBroadcast()
|
||||
}
|
||||
|
||||
private val mLogListener = LogListener { priority, msg ->
|
||||
logging?.addLogItem(priority, msg)
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder {
|
||||
return mBinder
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
if (intent?.action == SHUTDOWN_ACTION) {
|
||||
stop()
|
||||
} else {
|
||||
start()
|
||||
if (intent?.getBooleanExtra(
|
||||
"wakelock",
|
||||
false
|
||||
) == true
|
||||
) setWakelockEnabled(true)
|
||||
}
|
||||
return START_REDELIVER_INTENT
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
synchronized(this) {
|
||||
if (mAbort) return
|
||||
setStatus(MAIN_STATUS_STARTED, null)
|
||||
}
|
||||
Bridge.run(this, mLogListener)
|
||||
setStatus(MAIN_STATUS_STOPPED, null)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun setStatus(status: Int, error: String?) {
|
||||
mStatus = status
|
||||
mError = error
|
||||
sendMessage(MSG_SEND_STATUS, mStatus, 0, mError)
|
||||
}
|
||||
|
||||
private fun createNotificationBuilderWithChannel(): Notification.Builder? {
|
||||
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as? NotificationManager
|
||||
?: return null
|
||||
|
||||
val id = "org.musicpd"
|
||||
val name = "MPD service"
|
||||
val importance = 3 /* NotificationManager.IMPORTANCE_DEFAULT */
|
||||
|
||||
try {
|
||||
val ncClass = Class.forName("android.app.NotificationChannel")
|
||||
val ncCtor = ncClass.getConstructor(
|
||||
String::class.java,
|
||||
CharSequence::class.java,
|
||||
Int::class.javaPrimitiveType
|
||||
)
|
||||
val nc = ncCtor.newInstance(id, name, importance)
|
||||
|
||||
val nmCreateNotificationChannelMethod =
|
||||
NotificationManager::class.java.getMethod("createNotificationChannel", ncClass)
|
||||
nmCreateNotificationChannelMethod.invoke(notificationManager, nc)
|
||||
|
||||
val nbCtor: Constructor<*> = Notification.Builder::class.java.getConstructor(
|
||||
Context::class.java, String::class.java
|
||||
)
|
||||
return nbCtor.newInstance(this, id) as Notification.Builder
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "error creating the NotificationChannel", e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(markerClass = [UnstableApi::class])
|
||||
private fun start() {
|
||||
if (mThread != null) return
|
||||
|
||||
val filter = IntentFilter()
|
||||
filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
|
||||
registerReceiver(object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (!mPauseOnHeadphonesDisconnect) return
|
||||
if (intent.action === AudioManager.ACTION_AUDIO_BECOMING_NOISY) pause()
|
||||
}
|
||||
}, filter)
|
||||
|
||||
val mainIntent = Intent(this, MainActivity::class.java)
|
||||
mainIntent.setAction("android.intent.action.MAIN")
|
||||
mainIntent.addCategory("android.intent.category.LAUNCHER")
|
||||
val contentIntent = PendingIntent.getActivity(
|
||||
this, 0,
|
||||
mainIntent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val nBuilder: Notification.Builder?
|
||||
if (Build.VERSION.SDK_INT >= 26 /* Build.VERSION_CODES.O */) {
|
||||
nBuilder = createNotificationBuilderWithChannel()
|
||||
if (nBuilder == null) return
|
||||
} else nBuilder = Notification.Builder(this)
|
||||
|
||||
val notification =
|
||||
nBuilder.setContentTitle(getText(R.string.notification_title_mpd_running))
|
||||
.setContentText(getText(R.string.notification_text_mpd_running))
|
||||
.setSmallIcon(R.drawable.notification_icon)
|
||||
.setContentIntent(contentIntent)
|
||||
.build()
|
||||
|
||||
if (mpdLoader.isLoaded) {
|
||||
mThread = Thread(this).apply { start() }
|
||||
}
|
||||
|
||||
val player = MPDPlayer(Looper.getMainLooper())
|
||||
mMediaSession = MediaSession.Builder(this, player).build()
|
||||
|
||||
startForeground(R.string.notification_title_mpd_running, notification)
|
||||
startService(Intent(this, Main::class.java))
|
||||
}
|
||||
|
||||
private fun stop() {
|
||||
mMediaSession?.let {
|
||||
it.release()
|
||||
mMediaSession = null
|
||||
}
|
||||
mThread?.let { thread ->
|
||||
if (thread.isAlive) {
|
||||
synchronized(this) {
|
||||
if (mStatus == MAIN_STATUS_STARTED) Bridge.shutdown()
|
||||
else mAbort = true
|
||||
}
|
||||
}
|
||||
try {
|
||||
thread.join()
|
||||
mThread = null
|
||||
mAbort = false
|
||||
} catch (ie: InterruptedException) {
|
||||
Log.e(TAG, "failed to join", ie)
|
||||
}
|
||||
}
|
||||
setWakelockEnabled(false)
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
private fun pause() {
|
||||
if (mThread?.isAlive == true) {
|
||||
synchronized(this) {
|
||||
if (mStatus == MAIN_STATUS_STARTED) Bridge.pause()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setPauseOnHeadphonesDisconnect(enabled: Boolean) {
|
||||
mPauseOnHeadphonesDisconnect = enabled
|
||||
}
|
||||
|
||||
private fun setWakelockEnabled(enabled: Boolean) {
|
||||
if (enabled) {
|
||||
val wakeLock =
|
||||
mWakelock ?: run {
|
||||
val pm = getSystemService(POWER_SERVICE) as PowerManager
|
||||
pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG).also {
|
||||
mWakelock = it
|
||||
}
|
||||
}
|
||||
wakeLock.acquire(10 * 60 * 1000L /*10 minutes*/)
|
||||
Log.d(TAG, "Wakelock acquired")
|
||||
} else {
|
||||
mWakelock?.let {
|
||||
it.release()
|
||||
mWakelock = null
|
||||
}
|
||||
Log.d(TAG, "Wakelock released")
|
||||
}
|
||||
}
|
||||
|
||||
private val isRunning: Boolean
|
||||
get() = mThread?.isAlive == true
|
||||
|
||||
private fun registerCallback(cb: IMainCallback?) {
|
||||
if (cb != null) {
|
||||
mCallbacks.register(cb)
|
||||
sendMessage(MSG_SEND_STATUS, mStatus, 0, mError)
|
||||
}
|
||||
}
|
||||
|
||||
private fun unregisterCallback(cb: IMainCallback?) {
|
||||
if (cb != null) {
|
||||
mCallbacks.unregister(cb)
|
||||
}
|
||||
}
|
||||
}
|
57
android/app/src/main/java/org/musicpd/MainActivity.kt
Normal file
@@ -0,0 +1,57 @@
|
||||
package org.musicpd
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.core.view.WindowCompat
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.musicpd.ui.MPDApp
|
||||
import org.musicpd.ui.SettingsViewModel
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
private val settingsViewModel: SettingsViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
MPDApp(settingsViewModel = settingsViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun connectClient() {
|
||||
val client = MainServiceClient(this, object : MainServiceClient.Callback {
|
||||
override fun onStopped() {
|
||||
settingsViewModel.updateStatus("", false)
|
||||
}
|
||||
|
||||
override fun onStarted() {
|
||||
settingsViewModel.updateStatus("MPD Service Started", true)
|
||||
}
|
||||
|
||||
override fun onError(error: String) {
|
||||
settingsViewModel.removeClient()
|
||||
settingsViewModel.updateStatus(error, false)
|
||||
connectClient()
|
||||
}
|
||||
})
|
||||
|
||||
settingsViewModel.setClient(client)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
//mFirstRun = false
|
||||
connectClient()
|
||||
super.onStart()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
settingsViewModel.removeClient()
|
||||
super.onStop()
|
||||
}
|
||||
}
|
157
android/app/src/main/java/org/musicpd/MainServiceClient.java
Normal file
@@ -0,0 +1,157 @@
|
||||
package org.musicpd;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
|
||||
|
||||
/*
|
||||
* Client that bind the Main Service in order to send commands and receive callback
|
||||
*/
|
||||
public class MainServiceClient {
|
||||
|
||||
private static final String REMOTE_ERROR = "MPD process was killed";
|
||||
|
||||
public interface Callback {
|
||||
public void onStarted();
|
||||
public void onStopped();
|
||||
public void onError(String error);
|
||||
}
|
||||
|
||||
private boolean mBound = false;
|
||||
private final Context mContext;
|
||||
private Callback mCallback;
|
||||
private IMain mIMain = null;
|
||||
|
||||
private final IMainCallback.Stub mICallback = new IMainCallback.Stub() {
|
||||
|
||||
@Override
|
||||
public void onStopped() throws RemoteException {
|
||||
mCallback.onStopped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStarted() throws RemoteException {
|
||||
mCallback.onStarted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) throws RemoteException {
|
||||
mCallback.onError(error);
|
||||
}
|
||||
};
|
||||
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
synchronized (this) {
|
||||
mIMain = IMain.Stub.asInterface(service);
|
||||
try {
|
||||
if (mCallback != null)
|
||||
mIMain.registerCallback(mICallback);
|
||||
} catch (RemoteException e) {
|
||||
if (mCallback != null)
|
||||
mCallback.onError(REMOTE_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
if (mCallback != null)
|
||||
mCallback.onError(REMOTE_ERROR);
|
||||
}
|
||||
};
|
||||
|
||||
public MainServiceClient(Context context, Callback cb) throws IllegalArgumentException {
|
||||
if (context == null)
|
||||
throw new IllegalArgumentException("Context can't be null");
|
||||
mContext = context;
|
||||
mCallback = cb;
|
||||
mBound = mContext.bindService(new Intent(mContext, Main.class), mServiceConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
public boolean start() {
|
||||
synchronized (this) {
|
||||
if (mIMain != null) {
|
||||
try {
|
||||
mIMain.start();
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean stop() {
|
||||
synchronized (this) {
|
||||
if (mIMain != null) {
|
||||
try {
|
||||
mIMain.stop();
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setPauseOnHeadphonesDisconnect(boolean enabled) {
|
||||
synchronized (this) {
|
||||
if (mIMain != null) {
|
||||
try {
|
||||
mIMain.setPauseOnHeadphonesDisconnect(enabled);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setWakelockEnabled(boolean enabled) {
|
||||
synchronized (this) {
|
||||
if (mIMain != null) {
|
||||
try {
|
||||
mIMain.setWakelockEnabled(enabled);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
synchronized (this) {
|
||||
if (mIMain != null) {
|
||||
try {
|
||||
return mIMain.isRunning();
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
if (mBound) {
|
||||
synchronized (this) {
|
||||
if (mIMain != null && mICallback != null) {
|
||||
try {
|
||||
if (mCallback != null)
|
||||
mIMain.unregisterCallback(mICallback);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
mBound = false;
|
||||
mContext.unbindService(mServiceConnection);
|
||||
}
|
||||
}
|
||||
}
|
34
android/app/src/main/java/org/musicpd/NetworkUtil.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package org.musicpd;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class NetworkUtil {
|
||||
|
||||
@Nullable
|
||||
public static String getDeviceIPV4Address(Context context) {
|
||||
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
LinkProperties linkProperties = connectivityManager.getLinkProperties(connectivityManager.getActiveNetwork());
|
||||
if (linkProperties != null) {
|
||||
List<LinkAddress> linkAddresses = linkProperties.getLinkAddresses();
|
||||
for (LinkAddress address : linkAddresses) {
|
||||
if (!address.getAddress().isLinkLocalAddress() && !address.getAddress().isLoopbackAddress()) {
|
||||
InetAddress address1 = address.getAddress();
|
||||
if (address1 instanceof Inet4Address) {
|
||||
return address1.getHostAddress();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
34
android/app/src/main/java/org/musicpd/Preferences.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package org.musicpd;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class Preferences {
|
||||
private static final String TAG = "Settings";
|
||||
|
||||
public static final String KEY_RUN_ON_BOOT ="run_on_boot";
|
||||
public static final String KEY_WAKELOCK ="wakelock";
|
||||
public static final String KEY_PAUSE_ON_HEADPHONES_DISCONNECT ="pause_on_headphones_disconnect";
|
||||
|
||||
public static SharedPreferences get(Context context) {
|
||||
return context.getSharedPreferences(TAG, MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public static void putBoolean(Context context, String key, boolean value) {
|
||||
final SharedPreferences prefs = get(context);
|
||||
|
||||
if (prefs == null)
|
||||
return;
|
||||
final SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putBoolean(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static boolean getBoolean(Context context, String key, boolean defValue) {
|
||||
final SharedPreferences prefs = get(context);
|
||||
|
||||
return prefs != null ? prefs.getBoolean(key, defValue) : defValue;
|
||||
}
|
||||
}
|
34
android/app/src/main/java/org/musicpd/Receiver.java
Normal file
@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright The Music Player Daemon Project
|
||||
|
||||
package org.musicpd;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class Receiver extends BroadcastReceiver {
|
||||
|
||||
private static final Set<String> BOOT_ACTIONS = Set.of(
|
||||
"android.intent.action.BOOT_COMPLETED",
|
||||
"android.intent.action.QUICKBOOT_POWERON"
|
||||
);
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d("Receiver", "onReceive: " + intent);
|
||||
if (BOOT_ACTIONS.contains(intent.getAction())) {
|
||||
if (Preferences.getBoolean(context,
|
||||
Preferences.KEY_RUN_ON_BOOT,
|
||||
false)) {
|
||||
final boolean wakelock =
|
||||
Preferences.getBoolean(context,
|
||||
Preferences.KEY_WAKELOCK, false);
|
||||
Main.startService(context, wakelock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
package org.musicpd.data
|
||||
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
private const val MAX_LOGS = 500
|
||||
|
||||
@Singleton
|
||||
class LoggingRepository @Inject constructor() {
|
||||
|
||||
private val _logItemFLow = MutableStateFlow(listOf<String>())
|
||||
val logItemFLow: StateFlow<List<String>> = _logItemFLow
|
||||
|
||||
fun addLogItem(priority: Int, message: String) {
|
||||
if (_logItemFLow.value.size > MAX_LOGS) {
|
||||
_logItemFLow.value = _logItemFLow.value.drop(1)
|
||||
}
|
||||
|
||||
val priorityString: String = when (priority) {
|
||||
Log.DEBUG -> "D"
|
||||
Log.ERROR -> "E"
|
||||
Log.INFO -> "I"
|
||||
Log.VERBOSE -> "V"
|
||||
Log.WARN -> "W"
|
||||
else -> ""
|
||||
}
|
||||
|
||||
_logItemFLow.value = _logItemFLow.value + ("$priorityString/$message")
|
||||
}
|
||||
|
||||
}
|
79
android/app/src/main/java/org/musicpd/ui/LogScreen.kt
Normal file
@@ -0,0 +1,79 @@
|
||||
package org.musicpd.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.VerticalAlignBottom
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun LogView(messages: List<String>) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
var userScrolled = remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(lazyListState) {
|
||||
snapshotFlow { lazyListState.isScrollInProgress }
|
||||
.collect {
|
||||
if (it) {
|
||||
userScrolled.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
LazyColumn(
|
||||
Modifier.padding(4.dp),
|
||||
lazyListState
|
||||
) {
|
||||
items(messages) { message ->
|
||||
Text(text = message, fontFamily = FontFamily.Monospace)
|
||||
}
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
lazyListState.scrollToItem(messages.count(), 0)
|
||||
}
|
||||
}
|
||||
|
||||
if (lazyListState.canScrollForward) {
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
userScrolled.value = false
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
lazyListState.scrollToItem(messages.count(), 0)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(16.dp).align(Alignment.BottomEnd)
|
||||
) {
|
||||
Icon(Icons.Filled.VerticalAlignBottom, "Scroll to bottom icon")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun LogViewPreview() {
|
||||
val data = listOf("test",
|
||||
"test2",
|
||||
"test3")
|
||||
LogView(data)
|
||||
}
|
109
android/app/src/main/java/org/musicpd/ui/MainScreen.kt
Normal file
@@ -0,0 +1,109 @@
|
||||
package org.musicpd.ui
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.List
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
|
||||
enum class Screen {
|
||||
HOME,
|
||||
LOGS,
|
||||
SETTINGS,
|
||||
}
|
||||
sealed class NavigationItem(val route: String, val label: String, val icon: ImageVector) {
|
||||
data object Home : NavigationItem(
|
||||
Screen.HOME.name,
|
||||
"Home",
|
||||
Icons.Default.Home
|
||||
)
|
||||
data object Logs : NavigationItem(
|
||||
Screen.LOGS.name,
|
||||
"Logs",
|
||||
Icons.AutoMirrored.Filled.List)
|
||||
data object Settings : NavigationItem(
|
||||
Screen.SETTINGS.name,
|
||||
"Settings",
|
||||
Icons.Default.Settings)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MPDApp(
|
||||
navController: NavHostController = rememberNavController(),
|
||||
settingsViewModel: SettingsViewModel = viewModel()
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
|
||||
},
|
||||
bottomBar = {
|
||||
BottomNavigationBar(navController)
|
||||
},
|
||||
) { innerPadding ->
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = NavigationItem.Home.route,
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
) {
|
||||
composable(NavigationItem.Home.route) {
|
||||
StatusScreen(settingsViewModel)
|
||||
}
|
||||
composable(NavigationItem.Logs.route) {
|
||||
LogView(settingsViewModel.getLogs().collectAsStateWithLifecycle().value)
|
||||
}
|
||||
composable(NavigationItem.Settings.route) {
|
||||
MPDSettings(settingsViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BottomNavigationBar(navController: NavController) {
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentRoute = navBackStackEntry?.destination?.route
|
||||
|
||||
val items = listOf(
|
||||
NavigationItem.Home,
|
||||
NavigationItem.Logs,
|
||||
NavigationItem.Settings,
|
||||
)
|
||||
|
||||
NavigationBar {
|
||||
items.forEach { item ->
|
||||
NavigationBarItem(
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = item.icon,
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
label = { Text (item.label) },
|
||||
onClick = {
|
||||
navController.navigate(item.route) {
|
||||
popUpTo(navController.graph.startDestinationId)
|
||||
launchSingleTop = true
|
||||
}
|
||||
},
|
||||
selected = currentRoute == item.route,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
39
android/app/src/main/java/org/musicpd/ui/NetworkAddress.kt
Normal file
@@ -0,0 +1,39 @@
|
||||
package org.musicpd.ui
|
||||
|
||||
import android.app.Application
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Wifi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.musicpd.NetworkUtil
|
||||
|
||||
@Composable
|
||||
fun NetworkAddress() {
|
||||
val address = NetworkUtil.getDeviceIPV4Address(LocalContext.current)
|
||||
val padding = 4.dp
|
||||
Row(
|
||||
Modifier
|
||||
.padding(padding)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Wifi,
|
||||
contentDescription = "Wifi")
|
||||
Spacer(Modifier.size(padding))
|
||||
Text(text = address ?: "")
|
||||
}
|
||||
}
|
||||
|
77
android/app/src/main/java/org/musicpd/ui/SettingsScreen.kt
Normal file
@@ -0,0 +1,77 @@
|
||||
package org.musicpd.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.BatteryAlert
|
||||
import androidx.compose.material.icons.filled.Headphones
|
||||
import androidx.compose.material.icons.filled.PowerSettingsNew
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.alorma.compose.settings.storage.preferences.rememberPreferenceBooleanSettingState
|
||||
import com.alorma.compose.settings.ui.SettingsSwitch
|
||||
import org.musicpd.Preferences
|
||||
import org.musicpd.R
|
||||
|
||||
@Composable
|
||||
fun MPDSettings(settingsViewModel: SettingsViewModel) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
SettingsOptions(
|
||||
onBootChanged = { newValue ->
|
||||
if (newValue) {
|
||||
settingsViewModel.startMPD(context)
|
||||
}
|
||||
},
|
||||
onWakeLockChanged = { newValue ->
|
||||
settingsViewModel.setWakelockEnabled(newValue)
|
||||
},
|
||||
onHeadphonesChanged = { newValue ->
|
||||
settingsViewModel.setPauseOnHeadphonesDisconnect(newValue)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsOptions(
|
||||
onBootChanged: (Boolean) -> Unit,
|
||||
onWakeLockChanged: (Boolean) -> Unit,
|
||||
onHeadphonesChanged: (Boolean) -> Unit
|
||||
) {
|
||||
val bootState = rememberPreferenceBooleanSettingState(
|
||||
key = Preferences.KEY_RUN_ON_BOOT,
|
||||
defaultValue = false
|
||||
)
|
||||
val wakelockState =
|
||||
rememberPreferenceBooleanSettingState(key = Preferences.KEY_WAKELOCK, defaultValue = false)
|
||||
val headphoneState = rememberPreferenceBooleanSettingState(
|
||||
key = Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT,
|
||||
defaultValue = false
|
||||
)
|
||||
|
||||
SettingsSwitch(
|
||||
icon = { Icon(imageVector = Icons.Default.PowerSettingsNew, contentDescription = "Power") },
|
||||
title = { Text(text = stringResource(R.string.checkbox_run_on_boot)) },
|
||||
onCheckedChange = onBootChanged,
|
||||
state = bootState
|
||||
)
|
||||
SettingsSwitch(
|
||||
icon = { Icon(imageVector = Icons.Default.BatteryAlert, contentDescription = "Battery") },
|
||||
title = { Text(text = stringResource(R.string.checkbox_wakelock)) },
|
||||
onCheckedChange = onWakeLockChanged,
|
||||
state = wakelockState
|
||||
)
|
||||
SettingsSwitch(
|
||||
icon = { Icon(imageVector = Icons.Default.Headphones, contentDescription = "Headphones") },
|
||||
title = { Text(text = stringResource(R.string.checkbox_pause_on_headphones_disconnect)) },
|
||||
onCheckedChange = onHeadphonesChanged,
|
||||
state = headphoneState
|
||||
)
|
||||
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
package org.musicpd.ui
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import org.musicpd.Loader
|
||||
import org.musicpd.MainServiceClient
|
||||
import org.musicpd.Preferences
|
||||
import org.musicpd.data.LoggingRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@HiltViewModel
|
||||
class SettingsViewModel @Inject constructor(
|
||||
private var loggingRepository: LoggingRepository
|
||||
) : ViewModel() {
|
||||
private var mClient: MainServiceClient? = null
|
||||
val mpdLoader = Loader
|
||||
|
||||
data class StatusUiState(
|
||||
val statusMessage: String = "",
|
||||
val running: Boolean = false
|
||||
)
|
||||
|
||||
private val _statusUIState = MutableStateFlow(StatusUiState())
|
||||
val statusUIState: StateFlow<StatusUiState> = _statusUIState.asStateFlow()
|
||||
|
||||
fun getLogs(): StateFlow<List<String>> {
|
||||
return loggingRepository.logItemFLow
|
||||
}
|
||||
|
||||
fun updateStatus(message: String, running: Boolean) {
|
||||
_statusUIState.value = StatusUiState(message, running)
|
||||
}
|
||||
|
||||
fun setClient(client: MainServiceClient) {
|
||||
mClient = client
|
||||
}
|
||||
|
||||
fun removeClient() {
|
||||
mClient?.release()
|
||||
mClient = null
|
||||
}
|
||||
|
||||
fun startMPD(context: Context) {
|
||||
mClient?.start()
|
||||
if (Preferences.getBoolean(
|
||||
context,
|
||||
Preferences.KEY_WAKELOCK, false
|
||||
)
|
||||
) mClient?.setWakelockEnabled(true)
|
||||
if (Preferences.getBoolean(
|
||||
context,
|
||||
Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, false
|
||||
)
|
||||
) mClient?.setPauseOnHeadphonesDisconnect(true)
|
||||
}
|
||||
|
||||
fun stopMPD() {
|
||||
mClient?.stop()
|
||||
}
|
||||
|
||||
fun setWakelockEnabled(enabled: Boolean) {
|
||||
mClient?.setWakelockEnabled(enabled)
|
||||
}
|
||||
|
||||
fun setPauseOnHeadphonesDisconnect(enabled: Boolean) {
|
||||
mClient?.setPauseOnHeadphonesDisconnect(enabled)
|
||||
}
|
||||
}
|
188
android/app/src/main/java/org/musicpd/ui/StatusScreen.kt
Normal file
@@ -0,0 +1,188 @@
|
||||
package org.musicpd.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.TypedValue
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Circle
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.PermissionState
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.google.accompanist.permissions.shouldShowRationale
|
||||
import org.musicpd.R
|
||||
import org.musicpd.utils.openAppSettings
|
||||
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
fun StatusScreen(settingsViewModel: SettingsViewModel) {
|
||||
val storagePermissionState = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
rememberPermissionState(
|
||||
Manifest.permission.READ_MEDIA_AUDIO
|
||||
)
|
||||
} else {
|
||||
rememberPermissionState(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
Modifier
|
||||
.padding(4.dp)
|
||||
.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
NetworkAddress()
|
||||
ServerStatus(settingsViewModel, storagePermissionState)
|
||||
AudioMediaPermission(storagePermissionState)
|
||||
MPDLoaderStatus(settingsViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
fun getThemeColorAttribute(context: Context, @AttrRes attr: Int): Int {
|
||||
val value = TypedValue()
|
||||
if (context.theme.resolveAttribute(attr, value, true)) {
|
||||
return value.data
|
||||
}
|
||||
return android.graphics.Color.BLACK
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
fun ServerStatus(settingsViewModel: SettingsViewModel, storagePermissionState: PermissionState) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val statusUiState by settingsViewModel.statusUIState.collectAsState()
|
||||
|
||||
Column {
|
||||
Row(
|
||||
Modifier
|
||||
.padding(4.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Circle,
|
||||
contentDescription = "",
|
||||
tint = Color(
|
||||
getThemeColorAttribute(
|
||||
context,
|
||||
if (statusUiState.running) R.attr.appColorPositive else R.attr.appColorNegative
|
||||
)
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(end = 8.dp)
|
||||
.alpha(0.6f)
|
||||
)
|
||||
Text(text = stringResource(id = if (statusUiState.running) R.string.running else R.string.stopped))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if (statusUiState.running)
|
||||
settingsViewModel.stopMPD()
|
||||
else
|
||||
settingsViewModel.startMPD(context)
|
||||
},
|
||||
enabled = settingsViewModel.mpdLoader.isLoaded
|
||||
&& storagePermissionState.status.isGranted
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = if (statusUiState.running) R.string.stopMPD else R.string.startMPD)
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
Modifier
|
||||
.padding(4.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
Text(text = statusUiState.statusMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
fun AudioMediaPermission(storagePermissionState: PermissionState) {
|
||||
val permissionStatus = storagePermissionState.status
|
||||
if (!permissionStatus.isGranted) {
|
||||
val context = LocalContext.current
|
||||
Column(
|
||||
Modifier
|
||||
.padding(4.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
stringResource(id = R.string.external_files_permission_request),
|
||||
Modifier.padding(16.dp)
|
||||
)
|
||||
if (storagePermissionState.status.shouldShowRationale) {
|
||||
Button(onClick = {
|
||||
storagePermissionState.launchPermissionRequest()
|
||||
}) {
|
||||
Text("Request permission")
|
||||
}
|
||||
} else {
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
openAppSettings(context, context.packageName)
|
||||
},
|
||||
Modifier.padding(16.dp)
|
||||
) {
|
||||
Text(
|
||||
stringResource(id = R.string.title_open_app_info),
|
||||
color = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MPDLoaderStatus(settingsViewModel: SettingsViewModel) {
|
||||
val loader = settingsViewModel.mpdLoader
|
||||
if (!loader.isLoaded) {
|
||||
val context = LocalContext.current
|
||||
SelectionContainer {
|
||||
Text(
|
||||
loader.loadFailureMessage(context),
|
||||
Modifier.padding(16.dp),
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
32
android/app/src/main/java/org/musicpd/utils/IntentUtils.kt
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright The Music Player Daemon Project
|
||||
package org.musicpd.utils
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
|
||||
private const val TAG = "IntentUtils"
|
||||
|
||||
fun openAppSettings(
|
||||
context: Context,
|
||||
packageName: String
|
||||
) {
|
||||
try {
|
||||
context.startActivity(Intent().apply {
|
||||
setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
setData(Uri.parse("package:$packageName"))
|
||||
addCategory(Intent.CATEGORY_DEFAULT)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
|
||||
addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
|
||||
})
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e(
|
||||
TAG,
|
||||
"failed to open app settings for package: $packageName", e
|
||||
)
|
||||
}
|
||||
}
|
0
android/app/src/main/jnilibs/.keepme
Normal file
5
android/app/src/main/res/drawable/baseline_wifi_24.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M1,9l2,2c4.97,-4.97 13.03,-4.97 18,0l2,-2C16.93,2.93 7.08,2.93 1,9zM9,17l3,3 3,-3c-1.65,-1.66 -4.34,-1.66 -6,0zM5,13l2,2c2.76,-2.76 7.24,-2.76 10,0l2,-2C15.14,9.14 8.87,9.14 5,13z"/>
|
||||
</vector>
|
239
android/app/src/main/res/drawable/ic_banner_foreground.xml
Normal file
@@ -0,0 +1,239 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="320dp"
|
||||
android:height="180dp"
|
||||
android:viewportWidth="320"
|
||||
android:viewportHeight="180">
|
||||
<path
|
||||
android:pathData="M179.13,108.5H162.71V106.64C163.3,106.62 164.06,106.56 164.99,106.46C165.93,106.35 166.58,106.2 166.93,106.01C167.47,105.68 167.86,105.31 168.1,104.89C168.37,104.45 168.5,103.91 168.5,103.26V76.24H168.1L155.43,107.9H154.12L142.05,75.58H141.71V97.76C141.71,99.91 141.84,101.54 142.1,102.66C142.38,103.78 142.8,104.58 143.36,105.07C143.74,105.44 144.54,105.79 145.74,106.12C146.94,106.45 147.73,106.62 148.1,106.64V108.5H133.09V106.64C133.88,106.57 134.7,106.44 135.55,106.25C136.43,106.06 137.1,105.77 137.57,105.38C138.18,104.89 138.6,104.14 138.83,103.13C139.05,102.12 139.17,100.43 139.17,98.05V79.04C139.17,77.94 139.04,77.04 138.77,76.34C138.51,75.64 138.14,75.07 137.65,74.64C137.11,74.17 136.44,73.82 135.66,73.59C134.87,73.36 134.09,73.23 133.33,73.2V71.34H146L156.63,99.2L165.72,75.9C166.05,75.04 166.33,74.15 166.56,73.22C166.8,72.28 166.93,71.65 166.95,71.34H179.08V73.2C178.59,73.22 177.96,73.29 177.19,73.43C176.44,73.57 175.89,73.7 175.54,73.83C174.95,74.04 174.55,74.39 174.34,74.87C174.13,75.36 174.02,75.9 174.02,76.47V103.26C174.02,103.87 174.13,104.39 174.34,104.81C174.55,105.23 174.95,105.59 175.54,105.91C175.86,106.08 176.41,106.25 177.22,106.4C178.02,106.54 178.66,106.62 179.13,106.64V108.5Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M212.6,80.5C212.6,82.44 212.21,84.16 211.45,85.66C210.7,87.15 209.65,88.38 208.3,89.36C206.98,90.33 205.52,91.05 203.93,91.5C202.34,91.96 200.66,92.18 198.88,92.18H194.71V103.45C194.71,104.06 194.81,104.6 195,105.07C195.21,105.52 195.63,105.87 196.26,106.12C196.57,106.24 197.12,106.35 197.91,106.46C198.71,106.56 199.44,106.62 200.11,106.64V108.5H184.16V106.64C184.58,106.61 185.18,106.54 185.96,106.46C186.77,106.37 187.33,106.26 187.64,106.12C188.16,105.89 188.54,105.56 188.77,105.12C189.01,104.68 189.13,104.13 189.13,103.45V76.63C189.13,76.02 189.05,75.47 188.87,74.98C188.7,74.49 188.29,74.12 187.64,73.88C186.98,73.65 186.31,73.49 185.65,73.41C185.01,73.3 184.45,73.23 183.98,73.2V71.34H200.95C204.4,71.34 207.2,72.18 209.35,73.85C211.52,75.51 212.6,77.73 212.6,80.5ZM204.64,86.84C205.23,86 205.62,85.14 205.82,84.25C206.01,83.34 206.1,82.53 206.1,81.81C206.1,80.82 205.98,79.81 205.74,78.78C205.51,77.75 205.11,76.85 204.53,76.08C203.92,75.26 203.12,74.62 202.12,74.17C201.13,73.71 199.89,73.49 198.4,73.49H194.71V89.91H197.38C199.27,89.91 200.8,89.62 201.97,89.04C203.15,88.45 204.04,87.71 204.64,86.84Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M252.48,90.14C252.48,93.3 251.89,96.04 250.7,98.36C249.52,100.69 247.97,102.59 246.07,104.07C244.15,105.56 241.99,106.67 239.6,107.4C237.21,108.13 234.74,108.5 232.19,108.5H216.79V106.64C217.29,106.64 217.95,106.6 218.75,106.51C219.57,106.4 220.12,106.29 220.38,106.17C220.9,105.94 221.28,105.61 221.5,105.17C221.75,104.72 221.87,104.18 221.87,103.55V76.73C221.87,76.14 221.76,75.61 221.55,75.14C221.36,74.66 220.97,74.29 220.38,74.01C219.84,73.75 219.25,73.56 218.62,73.43C217.99,73.31 217.45,73.23 217,73.2V71.34H233.24C235.47,71.34 237.67,71.69 239.84,72.39C242,73.07 243.89,73.99 245.49,75.16C247.67,76.72 249.38,78.75 250.63,81.26C251.86,83.78 252.48,86.74 252.48,90.14ZM246.07,90.12C246.07,87.62 245.74,85.37 245.07,83.36C244.41,81.33 243.44,79.57 242.17,78.07C240.96,76.64 239.48,75.51 237.71,74.69C235.97,73.87 234.02,73.46 231.87,73.46C231.16,73.46 230.35,73.48 229.44,73.51C228.55,73.53 227.88,73.55 227.45,73.56V102.14C227.45,103.72 227.91,104.82 228.84,105.44C229.76,106.05 231.18,106.35 233.1,106.35C235.32,106.35 237.26,105.96 238.92,105.17C240.58,104.39 241.92,103.3 242.95,101.9C244.03,100.43 244.82,98.74 245.31,96.82C245.82,94.88 246.07,92.65 246.07,90.12Z"
|
||||
android:fillColor="#000000"/>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M23,51h71v71h-71z"/>
|
||||
<path
|
||||
android:pathData="M87.49,88.31C87.49,88.07 87.29,87.87 87.06,87.87H60.89C60.66,87.87 60.46,88.07 60.46,88.31V110.83C60.46,111.07 60.66,111.26 60.89,111.26H87.06C87.29,111.26 87.49,111.07 87.49,110.83V88.31Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.47"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M93.82,68.38C93.82,68.14 93.63,67.95 93.39,67.95H59.19C58.96,67.95 58.76,68.14 58.76,68.38V97.86C58.76,98.1 58.96,98.29 59.19,98.29H93.39C93.63,98.29 93.82,98.1 93.82,97.86V68.38Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.47"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M91.36,96.94C91.36,96.73 91.2,96.57 90.99,96.57H59.64C59.44,96.57 59.28,96.73 59.28,96.94V101.2C59.28,101.4 59.44,101.56 59.64,101.56H90.99C91.2,101.56 91.36,101.4 91.36,101.2V96.94Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.47"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M89.3,96.89C89.3,96.75 89.19,96.64 89.05,96.64H59.09C58.96,96.64 58.85,96.75 58.85,96.89V99.8C58.85,99.94 58.96,100.05 59.09,100.05H89.05C89.19,100.05 89.3,99.94 89.3,99.8V96.89Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="76.53"
|
||||
android:startY="102.12"
|
||||
android:endX="69.28"
|
||||
android:endY="95.45"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#BFD2D2D2"/>
|
||||
<item android:offset="1" android:color="#BFFFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M58.61,96.89C58.61,96.63 58.83,96.41 59.09,96.41H89.05C89.32,96.41 89.53,96.63 89.53,96.89V99.8C89.53,100.07 89.32,100.28 89.05,100.28H59.09C58.83,100.28 58.61,100.07 58.61,99.8V96.89ZM59.09,96.87C59.08,96.87 59.08,96.88 59.08,96.89V99.8C59.08,99.81 59.08,99.82 59.09,99.82H89.05C89.06,99.82 89.07,99.81 89.07,99.8V96.89C89.07,96.88 89.06,96.87 89.05,96.87H59.09Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M90.5,69.91C90.5,69.41 90.09,69 89.59,69H59.29C58.79,69 58.38,69.41 58.38,69.91V94.21C58.38,94.71 58.79,95.12 59.29,95.12H89.59C90.09,95.12 90.5,94.71 90.5,94.21V69.91Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="91.12"
|
||||
android:startY="94.89"
|
||||
android:endX="74.39"
|
||||
android:endY="79.87"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFD2D2D2"/>
|
||||
<item android:offset="1" android:color="#FFFFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M85.59,110.3C85.59,110.1 85.42,109.93 85.22,109.93H61.22C61.02,109.93 60.86,110.1 60.86,110.3V113.42C60.86,113.62 61.02,113.78 61.22,113.78H85.22C85.42,113.78 85.59,113.62 85.59,113.42V110.3Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.47"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M92.05,67.72C92.05,67.29 91.71,66.94 91.28,66.94H56.97C56.55,66.94 56.2,67.29 56.2,67.72V96.16C56.2,96.59 56.55,96.93 56.97,96.93H91.28C91.71,96.93 92.05,96.59 92.05,96.16V67.72Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="95.17"
|
||||
android:startY="93.03"
|
||||
android:endX="87.12"
|
||||
android:endY="79.61"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFD2D2D2"/>
|
||||
<item android:offset="1" android:color="#FFFFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M55.74,67.72C55.74,67.04 56.29,66.48 56.97,66.48H91.28C91.96,66.48 92.51,67.04 92.51,67.72V96.16C92.51,96.84 91.96,97.39 91.28,97.39H56.97C56.29,97.39 55.74,96.84 55.74,96.16V67.72ZM56.97,67.4C56.8,67.4 56.66,67.54 56.66,67.72V96.16C56.66,96.33 56.8,96.47 56.97,96.47H91.28C91.45,96.47 91.59,96.33 91.59,96.16V67.72C91.59,67.54 91.45,67.4 91.28,67.4H56.97Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M88.17,70.66C88.17,70.49 88.03,70.36 87.87,70.36H60.11C59.94,70.36 59.81,70.49 59.81,70.66V92.22C59.81,92.39 59.94,92.52 60.11,92.52H87.87C88.03,92.52 88.17,92.39 88.17,92.22V70.66Z"
|
||||
android:fillColor="#00B4ED"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M59.59,70.66C59.59,70.37 59.82,70.14 60.11,70.14H87.87C88.16,70.14 88.39,70.37 88.39,70.66V92.22C88.39,92.51 88.16,92.74 87.87,92.74H60.11C59.82,92.74 59.59,92.51 59.59,92.22V70.66ZM60.11,70.58C60.06,70.58 60.03,70.61 60.03,70.66V92.22C60.03,92.27 60.06,92.31 60.11,92.31H87.87C87.91,92.31 87.95,92.27 87.95,92.22V70.66C87.95,70.61 87.91,70.58 87.87,70.58H60.11Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M84.93,89.59C84.93,89.09 84.52,88.68 84.02,88.68H61.07C60.57,88.68 60.17,89.09 60.17,89.59V107.91C60.17,108.41 60.57,108.82 61.07,108.82H84.02C84.52,108.82 84.93,108.41 84.93,107.91V89.59Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="85.4"
|
||||
android:startY="108.64"
|
||||
android:endX="72.51"
|
||||
android:endY="97.06"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFD2D2D2"/>
|
||||
<item android:offset="1" android:color="#FFFFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M72.64,108.86C72.35,110.81 63.69,112.41 53.46,112.41C43.24,112.41 35.07,110.81 35.36,108.86C35.66,106.92 44.32,105.32 54.54,105.32C64.77,105.32 72.94,106.92 72.64,108.86Z"
|
||||
android:fillColor="#444040"
|
||||
android:fillAlpha="0.47"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M46.49,87.74C43.2,95.65 45.43,104.45 51.42,107.24C57.42,110.04 65.06,105.82 68.34,97.92C71.63,90.01 69.4,81.21 63.41,78.42C57.41,75.62 49.77,79.83 46.49,87.74Z"
|
||||
android:fillColor="#4E4D4B"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M68.58,86.44C69.43,89.79 69.19,93.78 67.61,97.58C66.03,101.38 63.41,104.26 60.51,105.86C57.6,107.47 54.45,107.76 51.73,106.49C49.01,105.23 47.09,102.57 46.25,99.22C45.4,95.87 45.64,91.88 47.22,88.08C48.8,84.28 51.42,81.4 54.32,79.79C57.23,78.19 60.37,77.9 63.1,79.17C65.82,80.43 67.74,83.09 68.58,86.44ZM70.13,86.06C69.18,82.34 66.99,79.19 63.72,77.67C60.44,76.14 56.77,76.56 53.54,78.34C50.31,80.12 47.46,83.29 45.75,87.4C44.04,91.5 43.76,95.87 44.7,99.6C45.65,103.32 47.84,106.47 51.11,107.99C54.39,109.52 58.06,109.1 61.29,107.32C64.52,105.54 67.37,102.37 69.08,98.26C70.78,94.15 71.07,89.79 70.13,86.06Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.95"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M30.48,75.66C23.76,88.96 25.17,103.77 33.61,108.47C42.05,113.17 54.53,106.08 61.25,92.78C67.98,79.48 66.56,64.67 58.12,59.98C49.68,55.28 37.21,62.36 30.48,75.66Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="89.23"
|
||||
android:startY="61.39"
|
||||
android:endX="8.66"
|
||||
android:endY="47.26"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFFFFFFF"/>
|
||||
<item android:offset="1" android:color="#FF000000"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M64.24,73.24C64.78,78.94 63.51,85.74 60.24,92.22C56.96,98.7 52.31,103.62 47.51,106.34C42.69,109.07 37.89,109.53 34.13,107.43C30.37,105.34 28.04,100.92 27.49,95.2C26.95,89.5 28.22,82.7 31.49,76.22C34.77,69.75 39.42,64.83 44.22,62.1C49.04,59.37 53.84,58.92 57.6,61.01C61.36,63.1 63.7,67.53 64.24,73.24ZM66.51,73.07C65.92,66.88 63.33,61.55 58.65,58.95C53.96,56.34 48.31,57.08 43.09,60.04C37.86,63 32.92,68.28 29.47,75.1C26.02,81.92 24.63,89.17 25.23,95.38C25.81,101.57 28.4,106.9 33.09,109.5C37.77,112.11 43.42,111.37 48.64,108.41C53.87,105.44 58.81,100.17 62.26,93.35C65.71,86.52 67.1,79.27 66.51,73.07Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.95"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M31.01,75.59C25.32,87.25 26.23,100.23 33.04,104.34C39.84,108.46 50.12,102.25 55.81,90.6C61.51,78.94 60.59,65.96 53.78,61.84C46.98,57.73 36.7,63.93 31.01,75.59Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:centerX="16.83"
|
||||
android:centerY="104.4"
|
||||
android:gradientRadius="32.44"
|
||||
android:type="radial">
|
||||
<item android:offset="0" android:color="#9EFFFFFF"/>
|
||||
<item android:offset="1" android:color="#FF5D6567"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M59.34,73.4C59.71,78.58 58.52,84.71 55.68,90.52C52.85,96.33 48.87,100.77 44.77,103.25C40.66,105.73 36.44,106.23 33.1,104.21C29.76,102.19 27.85,97.98 27.48,92.79C27.12,87.61 28.3,81.48 31.14,75.67C33.97,69.86 37.95,65.42 42.05,62.94C46.16,60.46 50.38,59.96 53.72,61.98C57.06,64 58.97,68.21 59.34,73.4ZM59.63,73.39C59.26,68.14 57.32,63.8 53.85,61.71C50.39,59.61 46.06,60.15 41.9,62.66C37.74,65.17 33.73,69.66 30.88,75.51C28.02,81.36 26.82,87.55 27.19,92.8C27.56,98.04 29.5,102.38 32.97,104.48C36.43,106.58 40.76,106.03 44.92,103.52C49.08,101.01 53.09,96.53 55.94,90.68C58.8,84.83 60,78.64 59.63,73.39Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.98"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M42.76,79.77C39.61,85.38 38.93,92.2 41.26,94.88C43.59,97.55 48.1,95.14 51.24,89.54C54.39,83.93 55.06,77.11 52.74,74.43C50.41,71.75 45.9,74.16 42.76,79.77Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M42.46,79.6C39.52,84.65 38.89,90.8 41.07,93.21C43.24,95.62 47.46,93.45 50.4,88.4C53.35,83.35 53.98,77.21 51.8,74.79C49.62,72.38 45.41,74.55 42.46,79.6Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="34.56"
|
||||
android:startY="87.9"
|
||||
android:endX="59.05"
|
||||
android:endY="97.29"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFD7D5D5"/>
|
||||
<item android:offset="1" android:color="#66000000"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M52.89,80.57C52.64,83.01 51.76,85.78 50.3,88.28C48.84,90.79 47.06,92.58 45.39,93.44C43.72,94.3 42.18,94.22 41.15,93.07C40.11,91.93 39.72,89.87 39.97,87.43C40.22,85 41.11,82.22 42.57,79.72C44.03,77.21 45.8,75.43 47.47,74.57C49.15,73.7 50.69,73.78 51.72,74.93C52.75,76.07 53.14,78.13 52.89,80.57ZM53.15,80.64C53.41,78.15 53.02,75.93 51.88,74.66C50.73,73.39 49.08,73.35 47.37,74.23C45.65,75.11 43.84,76.94 42.36,79.48C40.88,82.03 39.97,84.85 39.71,87.36C39.46,89.85 39.84,92.08 40.99,93.35C42.13,94.61 43.79,94.65 45.5,93.77C47.22,92.89 49.02,91.06 50.51,88.52C51.99,85.97 52.9,83.15 53.15,80.64Z"
|
||||
android:fillColor="#3F3B3B"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M84,110.24C84,110.1 83.89,109.99 83.75,109.99H60.77C60.64,109.99 60.53,110.1 60.53,110.24V112.37C60.53,112.5 60.64,112.61 60.77,112.61H83.75C83.89,112.61 84,112.5 84,112.37V110.24Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="74.16"
|
||||
android:startY="114.32"
|
||||
android:endX="68.55"
|
||||
android:endY="108.99"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#BFD2D2D2"/>
|
||||
<item android:offset="1" android:color="#BFFFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M60.3,110.24C60.3,109.97 60.51,109.76 60.77,109.76H83.75C84.02,109.76 84.23,109.97 84.23,110.24V112.37C84.23,112.63 84.02,112.85 83.75,112.85H60.77C60.51,112.85 60.3,112.63 60.3,112.37V110.24ZM60.77,110.22C60.76,110.22 60.76,110.23 60.76,110.24V112.37C60.76,112.38 60.76,112.39 60.77,112.39H83.75C83.76,112.39 83.77,112.38 83.77,112.37V110.24C83.77,110.23 83.76,110.22 83.75,110.22H60.77Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M86.12,87.87C86.12,87.44 85.78,87.1 85.35,87.1H59.26C58.83,87.1 58.49,87.44 58.49,87.87V109.44C58.49,109.87 58.83,110.21 59.26,110.21H85.35C85.78,110.21 86.12,109.87 86.12,109.44V87.87Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="88.53"
|
||||
android:startY="107.21"
|
||||
android:endX="82.32"
|
||||
android:endY="96.86"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFD2D2D2"/>
|
||||
<item android:offset="1" android:color="#FFFFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M58.13,87.87C58.13,87.25 58.64,86.74 59.26,86.74H85.35C85.97,86.74 86.48,87.25 86.48,87.87V109.44C86.48,110.06 85.97,110.57 85.35,110.57H59.26C58.64,110.57 58.13,110.06 58.13,109.44V87.87ZM59.26,87.45C59.03,87.45 58.84,87.64 58.84,87.87V109.44C58.84,109.67 59.03,109.86 59.26,109.86H85.35C85.58,109.86 85.77,109.67 85.77,109.44V87.87C85.77,87.64 85.58,87.45 85.35,87.45H59.26Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M83.13,90.18C83.13,90.02 82.99,89.88 82.83,89.88H61.57C61.4,89.88 61.27,90.02 61.27,90.18V106.67C61.27,106.83 61.4,106.97 61.57,106.97H82.83C82.99,106.97 83.13,106.83 83.13,106.67V90.18Z"
|
||||
android:fillColor="#003D88"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M61.1,90.18C61.1,89.92 61.31,89.71 61.57,89.71H82.83C83.09,89.71 83.3,89.92 83.3,90.18V106.67C83.3,106.93 83.09,107.14 82.83,107.14H61.57C61.31,107.14 61.1,106.93 61.1,106.67V90.18ZM61.57,90.05C61.5,90.05 61.44,90.11 61.44,90.18V106.67C61.44,106.74 61.5,106.8 61.57,106.8H82.83C82.9,106.8 82.96,106.74 82.96,106.67V90.18C82.96,90.11 82.9,90.05 82.83,90.05H61.57Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.93"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
</vector>
|
235
android/app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,235 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<group android:scaleX="0.7121053"
|
||||
android:scaleY="0.7121053"
|
||||
android:translateX="14.83421"
|
||||
android:translateY="16.25842">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M15,15h78v78h-78z"/>
|
||||
<path
|
||||
android:pathData="M85.85,55.98C85.85,55.72 85.63,55.51 85.37,55.51H56.63C56.37,55.51 56.15,55.72 56.15,55.98V80.73C56.15,80.99 56.37,81.2 56.63,81.2H85.37C85.63,81.2 85.85,80.99 85.85,80.73V55.98Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.47"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M92.81,34.1C92.81,33.83 92.59,33.62 92.33,33.62H54.76C54.5,33.62 54.29,33.83 54.29,34.1V66.48C54.29,66.74 54.5,66.96 54.76,66.96H92.33C92.59,66.96 92.81,66.74 92.81,66.48V34.1Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.47"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M90.1,65.47C90.1,65.24 89.92,65.06 89.7,65.06H55.26C55.03,65.06 54.85,65.24 54.85,65.47V70.15C54.85,70.37 55.03,70.55 55.26,70.55H89.7C89.92,70.55 90.1,70.37 90.1,70.15V65.47Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.47"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M87.84,65.41C87.84,65.26 87.72,65.14 87.57,65.14H54.65C54.5,65.14 54.38,65.26 54.38,65.41V68.61C54.38,68.76 54.5,68.89 54.65,68.89H87.57C87.72,68.89 87.84,68.76 87.84,68.61V65.41Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="73.8"
|
||||
android:startY="71.16"
|
||||
android:endX="65.84"
|
||||
android:endY="63.83"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#BFD2D2D2"/>
|
||||
<item android:offset="1" android:color="#BFFFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M54.13,65.41C54.13,65.12 54.36,64.89 54.65,64.89H87.57C87.86,64.89 88.09,65.12 88.09,65.41V68.61C88.09,68.9 87.86,69.14 87.57,69.14H54.65C54.36,69.14 54.13,68.9 54.13,68.61V65.41ZM54.65,65.39C54.64,65.39 54.63,65.4 54.63,65.41V68.61C54.63,68.62 54.64,68.63 54.65,68.63H87.57C87.58,68.63 87.59,68.62 87.59,68.61V65.41C87.59,65.4 87.58,65.39 87.57,65.39H54.65Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M89.15,35.77C89.15,35.22 88.71,34.78 88.16,34.78H54.86C54.31,34.78 53.87,35.22 53.87,35.77V62.47C53.87,63.02 54.31,63.47 54.86,63.47H88.16C88.71,63.47 89.15,63.02 89.15,62.47V35.77Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="89.84"
|
||||
android:startY="63.22"
|
||||
android:endX="71.46"
|
||||
android:endY="46.71"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFD2D2D2"/>
|
||||
<item android:offset="1" android:color="#FFFFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M83.76,80.15C83.76,79.92 83.58,79.74 83.36,79.74H56.99C56.77,79.74 56.59,79.92 56.59,80.15V83.57C56.59,83.79 56.77,83.97 56.99,83.97H83.36C83.58,83.97 83.76,83.79 83.76,83.57V80.15Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.47"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M90.86,33.37C90.86,32.9 90.48,32.51 90.01,32.51H52.32C51.85,32.51 51.47,32.9 51.47,33.37V64.61C51.47,65.08 51.85,65.46 52.32,65.46H90.01C90.48,65.46 90.86,65.08 90.86,64.61V33.37Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="94.29"
|
||||
android:startY="61.18"
|
||||
android:endX="85.44"
|
||||
android:endY="46.43"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFD2D2D2"/>
|
||||
<item android:offset="1" android:color="#FFFFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M50.97,33.37C50.97,32.62 51.58,32.01 52.32,32.01H90.01C90.76,32.01 91.37,32.62 91.37,33.37V64.61C91.37,65.36 90.76,65.97 90.01,65.97H52.32C51.58,65.97 50.97,65.36 50.97,64.61V33.37ZM52.32,33.02C52.13,33.02 51.98,33.17 51.98,33.37V64.61C51.98,64.8 52.13,64.96 52.32,64.96H90.01C90.2,64.96 90.36,64.8 90.36,64.61V33.37C90.36,33.17 90.2,33.02 90.01,33.02H52.32Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M86.59,36.59C86.59,36.41 86.45,36.26 86.26,36.26H55.77C55.59,36.26 55.44,36.41 55.44,36.59V60.29C55.44,60.47 55.59,60.62 55.77,60.62H86.26C86.45,60.62 86.59,60.47 86.59,60.29V36.59Z"
|
||||
android:fillColor="#00B4ED"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M55.2,36.59C55.2,36.28 55.45,36.02 55.77,36.02H86.26C86.58,36.02 86.83,36.28 86.83,36.59V60.29C86.83,60.6 86.58,60.86 86.26,60.86H55.77C55.45,60.86 55.2,60.6 55.2,60.29V36.59ZM55.77,36.51C55.72,36.51 55.68,36.55 55.68,36.59V60.29C55.68,60.34 55.72,60.38 55.77,60.38H86.26C86.31,60.38 86.35,60.34 86.35,60.29V36.59C86.35,36.55 86.31,36.51 86.26,36.51H55.77Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M83.03,57.4C83.03,56.85 82.59,56.4 82.04,56.4H56.83C56.28,56.4 55.83,56.85 55.83,57.4V77.52C55.83,78.07 56.28,78.52 56.83,78.52H82.04C82.59,78.52 83.03,78.07 83.03,77.52V57.4Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="83.56"
|
||||
android:startY="78.32"
|
||||
android:endX="69.39"
|
||||
android:endY="65.6"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFD2D2D2"/>
|
||||
<item android:offset="1" android:color="#FFFFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M69.54,78.57C69.21,80.71 59.7,82.46 48.47,82.46C37.23,82.46 28.26,80.71 28.58,78.57C28.91,76.43 38.42,74.68 49.65,74.68C60.89,74.68 69.87,76.43 69.54,78.57Z"
|
||||
android:fillColor="#444040"
|
||||
android:fillAlpha="0.47"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M40.8,55.36C37.19,64.05 39.64,73.72 46.22,76.79C52.81,79.86 61.2,75.23 64.81,66.55C68.42,57.86 65.98,48.19 59.39,45.12C52.8,42.05 44.41,46.68 40.8,55.36Z"
|
||||
android:fillColor="#4E4D4B"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M65.08,53.93C66.01,57.61 65.74,62 64.01,66.17C62.27,70.34 59.4,73.51 56.2,75.27C53.01,77.03 49.55,77.36 46.57,75.97C43.58,74.57 41.47,71.66 40.54,67.98C39.61,64.3 39.87,59.91 41.61,55.74C43.34,51.56 46.22,48.39 49.41,46.63C52.6,44.87 56.06,44.55 59.05,45.94C62.04,47.33 64.15,50.25 65.08,53.93ZM66.77,53.52C65.74,49.43 63.33,45.97 59.73,44.3C56.13,42.62 52.1,43.08 48.55,45.03C45,46.99 41.87,50.47 39.99,54.99C38.12,59.5 37.81,64.29 38.84,68.39C39.88,72.48 42.29,75.93 45.88,77.61C49.48,79.29 53.51,78.83 57.06,76.87C60.62,74.92 63.74,71.43 65.62,66.92C67.5,62.41 67.81,57.62 66.77,53.52Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.95"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M23.22,42.09C15.83,56.7 17.38,72.97 26.65,78.13C35.93,83.29 49.63,75.52 57.02,60.9C64.41,46.29 62.86,30.02 53.59,24.86C44.31,19.7 30.61,27.48 23.22,42.09Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="87.76"
|
||||
android:startY="26.41"
|
||||
android:endX="-0.75"
|
||||
android:endY="10.89"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFFFFFFF"/>
|
||||
<item android:offset="1" android:color="#FF000000"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M60.31,39.44C60.9,45.7 59.51,53.17 55.91,60.29C52.31,67.4 47.2,72.81 41.92,75.8C36.63,78.8 31.36,79.3 27.23,77C23.1,74.7 20.53,69.84 19.93,63.56C19.34,57.3 20.73,49.83 24.33,42.71C27.93,35.59 33.04,30.19 38.32,27.2C43.61,24.2 48.88,23.7 53.01,26C57.14,28.3 59.71,33.16 60.31,39.44ZM62.8,39.24C62.15,32.44 59.3,26.59 54.16,23.73C49.02,20.87 42.8,21.68 37.08,24.93C31.33,28.19 25.9,33.98 22.11,41.47C18.32,48.97 16.79,56.94 17.44,63.76C18.09,70.56 20.94,76.41 26.08,79.27C31.22,82.13 37.44,81.32 43.16,78.07C48.91,74.81 54.34,69.02 58.13,61.52C61.92,54.03 63.45,46.06 62.8,39.24Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.95"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M23.8,42.01C17.54,54.82 18.55,69.08 26.03,73.6C33.5,78.12 44.8,71.31 51.05,58.5C57.3,45.69 56.29,31.43 48.82,26.91C41.34,22.39 30.05,29.21 23.8,42.01Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:centerX="8.22"
|
||||
android:centerY="73.66"
|
||||
android:gradientRadius="35.64"
|
||||
android:type="radial">
|
||||
<item android:offset="0" android:color="#9EFFFFFF"/>
|
||||
<item android:offset="1" android:color="#FF5D6567"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M54.92,39.6C55.32,45.3 54.02,52.03 50.91,58.41C47.79,64.79 43.42,69.68 38.91,72.4C34.4,75.12 29.77,75.67 26.1,73.45C22.43,71.23 20.32,66.61 19.92,60.91C19.52,55.22 20.82,48.48 23.94,42.1C27.05,35.72 31.42,30.84 35.93,28.12C40.45,25.39 45.08,24.84 48.75,27.06C52.41,29.28 54.52,33.91 54.92,39.6ZM55.24,39.6C54.84,33.83 52.7,29.07 48.89,26.76C45.09,24.46 40.33,25.06 35.77,27.81C31.2,30.57 26.79,35.5 23.65,41.93C20.51,48.35 19.2,55.15 19.6,60.92C20.01,66.68 22.14,71.45 25.95,73.75C29.76,76.06 34.51,75.46 39.08,72.7C43.65,69.94 48.05,65.01 51.19,58.59C54.33,52.16 55.65,45.36 55.24,39.6Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.98"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M36.7,46.61C33.25,52.77 32.51,60.26 35.06,63.2C37.62,66.14 42.57,63.5 46.03,57.34C49.48,51.18 50.22,43.69 47.67,40.74C45.11,37.8 40.16,40.45 36.7,46.61Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M36.38,46.42C33.15,51.97 32.46,58.72 34.85,61.37C37.24,64.02 41.87,61.64 45.1,56.09C48.34,50.54 49.03,43.79 46.64,41.14C44.25,38.49 39.62,40.87 36.38,46.42Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="27.7"
|
||||
android:startY="55.54"
|
||||
android:endX="54.6"
|
||||
android:endY="65.85"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFD7D5D5"/>
|
||||
<item android:offset="1" android:color="#66000000"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M47.84,47.49C47.56,50.16 46.59,53.21 44.99,55.96C43.38,58.71 41.44,60.68 39.6,61.62C37.76,62.57 36.07,62.48 34.93,61.22C33.8,59.96 33.37,57.7 33.65,55.02C33.92,52.35 34.89,49.3 36.5,46.55C38.1,43.8 40.05,41.83 41.89,40.89C43.73,39.94 45.42,40.03 46.55,41.29C47.69,42.55 48.11,44.8 47.84,47.49ZM48.12,47.57C48.41,44.82 47.98,42.38 46.73,40.99C45.47,39.6 43.65,39.55 41.77,40.52C39.88,41.49 37.9,43.5 36.27,46.29C34.64,49.09 33.64,52.19 33.36,54.94C33.08,57.69 33.5,60.13 34.76,61.52C36.02,62.91 37.84,62.96 39.72,61.99C41.61,61.02 43.59,59.01 45.22,56.22C46.85,53.42 47.84,50.32 48.12,47.57Z"
|
||||
android:fillColor="#3F3B3B"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M82.02,80.08C82.02,79.93 81.9,79.8 81.74,79.8H56.5C56.35,79.8 56.23,79.93 56.23,80.08V82.42C56.23,82.57 56.35,82.69 56.5,82.69H81.74C81.9,82.69 82.02,82.57 82.02,82.42V80.08Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="71.21"
|
||||
android:startY="84.56"
|
||||
android:endX="65.04"
|
||||
android:endY="78.7"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#BFD2D2D2"/>
|
||||
<item android:offset="1" android:color="#BFFFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M55.97,80.08C55.97,79.79 56.21,79.55 56.5,79.55H81.74C82.04,79.55 82.27,79.79 82.27,80.08V82.42C82.27,82.71 82.04,82.94 81.74,82.94H56.5C56.21,82.94 55.97,82.71 55.97,82.42V80.08ZM56.5,80.06C56.49,80.06 56.48,80.07 56.48,80.08V82.42C56.48,82.43 56.49,82.44 56.5,82.44H81.74C81.76,82.44 81.76,82.43 81.76,82.42V80.08C81.76,80.07 81.76,80.06 81.74,80.06H56.5Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M84.35,55.51C84.35,55.04 83.96,54.65 83.5,54.65H54.84C54.37,54.65 53.99,55.04 53.99,55.51V79.2C53.99,79.67 54.37,80.05 54.84,80.05H83.5C83.96,80.05 84.35,79.67 84.35,79.2V55.51Z"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="86.99"
|
||||
android:startY="76.75"
|
||||
android:endX="80.17"
|
||||
android:endY="65.38"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFD2D2D2"/>
|
||||
<item android:offset="1" android:color="#FFFFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M53.6,55.51C53.6,54.82 54.15,54.26 54.84,54.26H83.5C84.18,54.26 84.74,54.82 84.74,55.51V79.2C84.74,79.89 84.18,80.44 83.5,80.44H54.84C54.15,80.44 53.6,79.89 53.6,79.2V55.51ZM54.84,55.04C54.58,55.04 54.38,55.25 54.38,55.51V79.2C54.38,79.45 54.58,79.66 54.84,79.66H83.5C83.75,79.66 83.96,79.45 83.96,79.2V55.51C83.96,55.25 83.75,55.04 83.5,55.04H54.84Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M81.06,58.05C81.06,57.86 80.91,57.72 80.73,57.72H57.37C57.19,57.72 57.04,57.86 57.04,58.05V76.16C57.04,76.34 57.19,76.49 57.37,76.49H80.73C80.91,76.49 81.06,76.34 81.06,76.16V58.05Z"
|
||||
android:fillColor="#003D88"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M56.85,58.05C56.85,57.76 57.09,57.53 57.37,57.53H80.73C81.01,57.53 81.24,57.76 81.24,58.05V76.16C81.24,76.44 81.01,76.67 80.73,76.67H57.37C57.09,76.67 56.85,76.44 56.85,76.16V58.05ZM57.37,57.9C57.29,57.9 57.23,57.97 57.23,58.05V76.16C57.23,76.24 57.29,76.3 57.37,76.3H80.73C80.81,76.3 80.87,76.24 80.87,76.16V58.05C80.87,57.97 80.81,57.9 80.73,57.9H57.37Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.93"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
</group>
|
||||
</vector>
|
BIN
android/app/src/main/res/drawable/notification_icon.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
5
android/app/src/main/res/mipmap-anydpi-v26/ic_banner.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_banner_background"/>
|
||||
<foreground android:drawable="@drawable/ic_banner_foreground"/>
|
||||
</adaptive-icon>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
BIN
android/app/src/main/res/mipmap-hdpi/ic_banner.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_banner.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_banner.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_banner.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_banner.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 10 KiB |
4
android/app/src/main/res/values/ic_banner_background.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_banner_background">#FFFFFF</color>
|
||||
</resources>
|
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
25
android/app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<string name="app_name">MPD</string>
|
||||
<string name="notification_title_mpd_running">Music Player Daemon is running</string>
|
||||
<string name="notification_text_mpd_running">Touch for MPD options.</string>
|
||||
<string name="toggle_button_run_on">MPD is running</string>
|
||||
<string name="toggle_button_run_off">MPD is not running</string>
|
||||
<string name="checkbox_run_on_boot">Run MPD automatically on boot</string>
|
||||
<string name="checkbox_wakelock">Prevent suspend when MPD is running (Wakelock)</string>
|
||||
<string name="checkbox_pause_on_headphones_disconnect">Pause MPD when headphones disconnect</string>
|
||||
<string name="external_files_permission_request">MPD requires access to external files to play local music. Please grant the permission.</string>
|
||||
<string name="title_open_app_info">Open app info</string>
|
||||
<string name="mpd_load_failure_message">"Failed to load the native MPD library.
|
||||
Report this problem to us, and include the following information:
|
||||
SUPPORTED_ABIS=%1$s
|
||||
PRODUCT=%2$s
|
||||
FINGERPRINT=%3$s
|
||||
error=%4$s"
|
||||
</string>
|
||||
<string name="stopped">Stopped</string>
|
||||
<string name="running">Running</string>
|
||||
<string name="stopMPD">Stop MPD</string>
|
||||
<string name="startMPD">Start MPD</string>
|
||||
</resources>
|
21
android/app/src/main/res/values/themes.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="red_500">#F44336</color>
|
||||
<color name="red_900">#B71C1C</color>
|
||||
<color name="green_300">#81C784</color>
|
||||
<color name="green_700">#388E3C</color>
|
||||
|
||||
<color name="colorErrorOnLight">@color/red_900</color>
|
||||
<color name="colorErrorOnDark">@color/red_500</color>
|
||||
|
||||
<color name="colorSuccessOnLight">@color/green_700</color>
|
||||
<color name="colorSuccessOnDark">@color/green_300</color>
|
||||
|
||||
<attr name="appColorNegative" format="color|reference" />
|
||||
<attr name="appColorPositive" format="color|reference" />
|
||||
|
||||
<style name="Theme.MPD" parent="android:Theme.Material.Light.NoActionBar">
|
||||
<item name="appColorNegative">@color/colorErrorOnLight</item>
|
||||
<item name="appColorPositive">@color/colorSuccessOnLight</item>
|
||||
</style>
|
||||
</resources>
|
7
android/build.gradle.kts
Normal file
@@ -0,0 +1,7 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.google.devtools.ksp") version "1.9.22-1.0.16" apply false
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
||||
alias(libs.plugins.dagger.hilt.android) apply false
|
||||
}
|
177
android/build.py
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env -S python3 -u
|
||||
|
||||
import os, os.path
|
||||
import shutil
|
||||
import sys, subprocess
|
||||
|
||||
if len(sys.argv) < 4:
|
||||
@@ -12,183 +13,45 @@ ndk_path = sys.argv[2]
|
||||
android_abi = sys.argv[3]
|
||||
configure_args = sys.argv[4:]
|
||||
|
||||
if not os.path.isfile(os.path.join(sdk_path, 'tools', 'android')):
|
||||
print("SDK not found in", ndk_path, file=sys.stderr)
|
||||
if not os.path.isfile(os.path.join(sdk_path, 'licenses', 'android-sdk-license')):
|
||||
print("SDK not found in", sdk_path, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.isdir(ndk_path):
|
||||
print("NDK not found in", ndk_path, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
android_abis = {
|
||||
'armeabi-v7a': {
|
||||
'arch': 'arm-linux-androideabi',
|
||||
'ndk_arch': 'arm',
|
||||
'toolchain_arch': 'arm-linux-androideabi',
|
||||
'llvm_triple': 'armv7-linux-androideabi',
|
||||
'cflags': '-fpic -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp',
|
||||
},
|
||||
|
||||
'arm64-v8a': {
|
||||
'arch': 'aarch64-linux-android',
|
||||
'ndk_arch': 'arm64',
|
||||
'toolchain_arch': 'aarch64-linux-android',
|
||||
'llvm_triple': 'aarch64-linux-android',
|
||||
'cflags': '-fpic',
|
||||
},
|
||||
|
||||
'x86': {
|
||||
'arch': 'i686-linux-android',
|
||||
'ndk_arch': 'x86',
|
||||
'toolchain_arch': 'x86',
|
||||
'llvm_triple': 'i686-linux-android',
|
||||
'cflags': '-fPIC -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
|
||||
},
|
||||
|
||||
'x86_64': {
|
||||
'arch': 'x86_64-linux-android',
|
||||
'ndk_arch': 'x86_64',
|
||||
'toolchain_arch': 'x86_64',
|
||||
'llvm_triple': 'x86_64-linux-android',
|
||||
'cflags': '-fPIC -m64',
|
||||
},
|
||||
}
|
||||
|
||||
# select the NDK target
|
||||
abi_info = android_abis[android_abi]
|
||||
arch = abi_info['arch']
|
||||
|
||||
# the path to the MPD sources
|
||||
mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..'))
|
||||
sys.path[0] = os.path.join(mpd_path, 'python')
|
||||
|
||||
# output directories
|
||||
from build.dirs import lib_path, tarball_path, src_path
|
||||
from build.meson import configure as run_meson
|
||||
|
||||
arch_path = os.path.join(lib_path, arch)
|
||||
build_path = os.path.join(arch_path, 'build')
|
||||
|
||||
# build host configuration
|
||||
build_arch = 'linux-x86_64'
|
||||
|
||||
# set up the NDK toolchain
|
||||
|
||||
class AndroidNdkToolchain:
|
||||
def __init__(self, tarball_path, src_path, build_path,
|
||||
use_cxx):
|
||||
self.tarball_path = tarball_path
|
||||
self.src_path = src_path
|
||||
self.build_path = build_path
|
||||
|
||||
ndk_arch = abi_info['ndk_arch']
|
||||
android_api_level = '21'
|
||||
|
||||
# select the NDK compiler
|
||||
gcc_version = '4.9'
|
||||
|
||||
install_prefix = os.path.join(arch_path, 'root')
|
||||
|
||||
self.arch = arch
|
||||
self.install_prefix = install_prefix
|
||||
self.toolchain_arch = abi_info['toolchain_arch']
|
||||
|
||||
toolchain_path = os.path.join(ndk_path, 'toolchains', self.toolchain_arch + '-' + gcc_version, 'prebuilt', build_arch)
|
||||
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
|
||||
llvm_triple = abi_info['llvm_triple'] + android_api_level
|
||||
|
||||
common_flags = '-Os -g'
|
||||
common_flags += ' ' + abi_info['cflags']
|
||||
|
||||
toolchain_bin = os.path.join(toolchain_path, 'bin')
|
||||
llvm_bin = os.path.join(llvm_path, 'bin')
|
||||
self.cc = os.path.join(llvm_bin, 'clang')
|
||||
self.cxx = os.path.join(llvm_bin, 'clang++')
|
||||
common_flags += ' -target ' + llvm_triple + ' -gcc-toolchain ' + toolchain_path
|
||||
|
||||
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
|
||||
|
||||
# required flags from https://android.googlesource.com/platform/ndk/+/ndk-release-r20/docs/BuildSystemMaintainers.md#additional-required-arguments
|
||||
common_flags += ' -fno-addrsig'
|
||||
|
||||
self.ar = os.path.join(toolchain_bin, arch + '-ar')
|
||||
self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib')
|
||||
self.nm = os.path.join(toolchain_bin, arch + '-nm')
|
||||
self.strip = os.path.join(toolchain_bin, arch + '-strip')
|
||||
|
||||
self.cflags = common_flags
|
||||
self.cxxflags = common_flags
|
||||
self.cppflags = ' -isystem ' + os.path.join(install_prefix, 'include')
|
||||
self.ldflags = ' -L' + os.path.join(install_prefix, 'lib') + \
|
||||
' -Wl,--exclude-libs=ALL' + \
|
||||
' ' + common_flags
|
||||
self.ldflags = common_flags
|
||||
self.libs = ''
|
||||
|
||||
self.is_arm = ndk_arch == 'arm'
|
||||
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
|
||||
self.is_aarch64 = ndk_arch == 'arm64'
|
||||
self.is_windows = False
|
||||
|
||||
libstdcxx_flags = ''
|
||||
libstdcxx_cxxflags = ''
|
||||
libstdcxx_ldflags = ''
|
||||
libstdcxx_libs = '-static-libstdc++'
|
||||
|
||||
if self.is_armv7:
|
||||
# On 32 bit ARM, clang generates no ".eh_frame" section;
|
||||
# instead, the LLVM unwinder library is used for unwinding
|
||||
# the stack after a C++ exception was thrown
|
||||
libstdcxx_libs += ' -lunwind'
|
||||
|
||||
if use_cxx:
|
||||
self.cxxflags += ' ' + libstdcxx_cxxflags
|
||||
self.ldflags += ' ' + libstdcxx_ldflags
|
||||
self.libs += ' ' + libstdcxx_libs
|
||||
|
||||
self.env = dict(os.environ)
|
||||
|
||||
# redirect pkg-config to use our root directory instead of the
|
||||
# default one on the build host
|
||||
import shutil
|
||||
bin_dir = os.path.join(install_prefix, 'bin')
|
||||
try:
|
||||
os.makedirs(bin_dir)
|
||||
except:
|
||||
pass
|
||||
self.pkg_config = shutil.copy(os.path.join(mpd_path, 'build', 'pkg-config.sh'),
|
||||
os.path.join(bin_dir, 'pkg-config'))
|
||||
self.env['PKG_CONFIG'] = self.pkg_config
|
||||
from build.toolchain import AndroidNdkToolchain
|
||||
|
||||
# a list of third-party libraries to be used by MPD on Android
|
||||
from build.libs import *
|
||||
thirdparty_libs = [
|
||||
libmpdclient,
|
||||
libogg,
|
||||
libvorbis,
|
||||
opus,
|
||||
flac,
|
||||
libid3tag,
|
||||
libmodplug,
|
||||
wildmidi,
|
||||
gme,
|
||||
ffmpeg,
|
||||
openssl,
|
||||
curl,
|
||||
libexpat,
|
||||
libnfs,
|
||||
boost,
|
||||
]
|
||||
|
||||
# build the third-party libraries
|
||||
for x in thirdparty_libs:
|
||||
toolchain = AndroidNdkToolchain(tarball_path, src_path, build_path,
|
||||
toolchain = AndroidNdkToolchain(mpd_path, lib_path,
|
||||
tarball_path, src_path,
|
||||
ndk_path, android_abi,
|
||||
use_cxx=x.use_cxx)
|
||||
if not x.is_installed(toolchain):
|
||||
x.build(toolchain)
|
||||
|
||||
# configure and build MPD
|
||||
toolchain = AndroidNdkToolchain(tarball_path, src_path, build_path,
|
||||
toolchain = AndroidNdkToolchain(mpd_path, lib_path,
|
||||
tarball_path, src_path,
|
||||
ndk_path, android_abi,
|
||||
use_cxx=True)
|
||||
|
||||
configure_args += [
|
||||
@@ -196,8 +59,24 @@ configure_args += [
|
||||
'-Dandroid_ndk=' + ndk_path,
|
||||
'-Dandroid_abi=' + android_abi,
|
||||
'-Dandroid_strip=' + toolchain.strip,
|
||||
'-Dopenssl:asm=disabled',
|
||||
'-Dwrap_mode=forcefallback'
|
||||
]
|
||||
|
||||
from build.meson import configure as run_meson
|
||||
run_meson(toolchain, mpd_path, '.', configure_args)
|
||||
subprocess.check_call(['/usr/bin/ninja'], env=toolchain.env)
|
||||
|
||||
ninja = shutil.which("ninja")
|
||||
subprocess.check_call([ninja], env=toolchain.env)
|
||||
|
||||
subprocess.check_call([ninja, 'install'], env=toolchain.env)
|
||||
|
||||
print("""
|
||||
-------------------------------------
|
||||
## To build the android app:
|
||||
# cd ../../android
|
||||
# ./gradlew assemble{}Debug
|
||||
## or, for a universal apk (includes both arm64-v8a and x86_64)
|
||||
# ./gradlew assembleUniversalDebug
|
||||
-------------------------------------
|
||||
""".format(android_abi.capitalize()))
|
54
android/gdb.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script need the following modification in ANDROID_NDK in order to attach
|
||||
# to the good :main pid
|
||||
#--- a/prebuilt/linux-x86_64/bin/ndk-gdb.py
|
||||
#+++ b/prebuilt/linux-x86_64/bin/ndk-gdb.py
|
||||
#@@ -669,7 +669,7 @@
|
||||
# log("Sleeping for {} seconds.".format(args.delay))
|
||||
# time.sleep(args.delay)
|
||||
#
|
||||
#- pids = gdbrunner.get_pids(device, pkg_name)
|
||||
#+ pids = gdbrunner.get_pids(device, pkg_name + ":main")
|
||||
# if len(pids) == 0:
|
||||
# error("Failed to find running process '{}'".format(pkg_name))
|
||||
# if len(pids) > 1:
|
||||
|
||||
SCRIPT_PATH=$(dirname $0)
|
||||
BUILD_PATH="`pwd`"
|
||||
TMP_PATH="$BUILD_PATH/gdb"
|
||||
NDK_GDB_ARGS="--force"
|
||||
ANDROID_NDK=$1
|
||||
|
||||
if [ ! -f $ANDROID_NDK/source.properties ];then
|
||||
echo "usage: $0 ANDROID_NDK"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f $BUILD_PATH/libmpd.so ];then
|
||||
echo "This script need to be executed from the android build directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf "$TMP_PATH"
|
||||
mkdir -p "$TMP_PATH"
|
||||
|
||||
ANDROID_MANIFEST="$SCRIPT_PATH/AndroidManifest.xml"
|
||||
ABI=`ls "$BUILD_PATH/android/apk/apk/lib" --sort=time | head -n 1`
|
||||
|
||||
if [ ! -f "$ANDROID_MANIFEST" -o "$ABI" = "" ]; then
|
||||
echo "Invalid manifest/ABI, did you try building first ?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$TMP_PATH"/jni
|
||||
touch "$TMP_PATH"/jni/Android.mk
|
||||
echo "APP_ABI := $ABI" > "$TMP_PATH"/jni/Application.mk
|
||||
|
||||
DEST=obj/local/$ABI
|
||||
mkdir -p "$TMP_PATH/$DEST"
|
||||
|
||||
cp "$BUILD_PATH/libmpd.so" "$TMP_PATH/$DEST"
|
||||
cp "$ANDROID_MANIFEST" "$TMP_PATH"
|
||||
|
||||
(cd "$TMP_PATH" && bash $ANDROID_NDK/ndk-gdb $NDK_GDB_ARGS)
|
21
android/gradle.properties
Normal file
@@ -0,0 +1,21 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
41
android/gradle/libs.versions.toml
Normal file
@@ -0,0 +1,41 @@
|
||||
[versions]
|
||||
androidGradlePlugin = "8.5.2"
|
||||
accompanistPermissions = "0.33.2-alpha"
|
||||
activityCompose = "1.10.0"
|
||||
appcompat = "1.7.0"
|
||||
composeBom = "2025.01.01"
|
||||
composeSettingsStoragePreferences = "1.0.3"
|
||||
composeSettingsUiM3 = "1.0.3"
|
||||
daggerCompiler = "2.49"
|
||||
hiltAndroid = "2.49"
|
||||
hiltCompiler = "2.49"
|
||||
lifecycleRuntimeKtx = "2.8.7"
|
||||
media3Session = "1.5.1"
|
||||
navigationCompose = "2.8.6"
|
||||
kotlin = "1.9.22"
|
||||
|
||||
[libraries]
|
||||
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" }
|
||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" }
|
||||
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
|
||||
androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" }
|
||||
androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" }
|
||||
androidx-material3 = { module = "androidx.compose.material3:material3" }
|
||||
androidx-media3-session = { module = "androidx.media3:media3-session", version.ref = "media3Session" }
|
||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
||||
androidx-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" }
|
||||
androidx-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
|
||||
androidx-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
|
||||
compose-settings-storage-preferences = { module = "com.github.alorma:compose-settings-storage-preferences", version.ref = "composeSettingsStoragePreferences" }
|
||||
compose-settings-ui-m3 = { module = "com.github.alorma:compose-settings-ui-m3", version.ref = "composeSettingsUiM3" }
|
||||
dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "daggerCompiler" }
|
||||
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
|
||||
hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hiltCompiler" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
|
||||
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hiltAndroid" }
|
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Sun Dec 17 15:00:03 CST 2023
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
185
android/gradlew
vendored
Executable file
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
89
android/gradlew.bat
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
21
android/include/meson.build
Normal file
@@ -0,0 +1,21 @@
|
||||
javac = find_program('javac')
|
||||
|
||||
bridge_header = custom_target(
|
||||
'org_musicpd_Bridge.h',
|
||||
output: 'org_musicpd_Bridge.h',
|
||||
input: [
|
||||
'../app/src/main/java/org/musicpd/Bridge.java',
|
||||
],
|
||||
command: [
|
||||
javac,
|
||||
'-source', '1.8', '-target', '1.8',
|
||||
'-Xlint:-options',
|
||||
'-cp', join_paths(android_sdk_platform_dir, 'android.jar'),
|
||||
|
||||
# not interested in *.class, we only want the JNI header
|
||||
'-d', '@PRIVATE_DIR@',
|
||||
|
||||
'-h', '@OUTDIR@',
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
@@ -1,24 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
S=`dirname "$0"`
|
||||
AAPT=$1
|
||||
BASE_JAR=$2
|
||||
JAVA_PKG=$3
|
||||
JAVA_PKG_PATH=$4
|
||||
APK_FILE="$5"
|
||||
D=`dirname "$APK_FILE"`
|
||||
|
||||
rm -rf "$D/res"
|
||||
mkdir -p "$D/res/drawable" "$D/src"
|
||||
cp "$D/icon.png" "$D/notification_icon.png" "$D/res/drawable/"
|
||||
|
||||
"$AAPT" package -f -m --auto-add-overlay \
|
||||
--custom-package "$JAVA_PKG" \
|
||||
-M "$S/AndroidManifest.xml" \
|
||||
-S "$D/res" \
|
||||
-S "$S/res" \
|
||||
-J "$D/src" \
|
||||
-I "$BASE_JAR" \
|
||||
-F "$D/resources.apk"
|
||||
|
||||
cp "$D/src/$JAVA_PKG_PATH/R.java" "$D/"
|
@@ -5,136 +5,13 @@ android_ndk = get_option('android_ndk')
|
||||
android_sdk = get_option('android_sdk')
|
||||
android_abi = get_option('android_abi')
|
||||
|
||||
android_sdk_build_tools_version = '29.0.3'
|
||||
android_sdk_platform = 'android-29'
|
||||
android_sdk_build_tools_version = '34.0.0'
|
||||
android_sdk_platform = 'android-34'
|
||||
|
||||
android_build_tools_dir = join_paths(android_sdk, 'build-tools', android_sdk_build_tools_version)
|
||||
android_sdk_platform_dir = join_paths(android_sdk, 'platforms', android_sdk_platform)
|
||||
|
||||
android_aidl = join_paths(android_build_tools_dir, 'aidl')
|
||||
android_aapt = join_paths(android_build_tools_dir, 'aapt')
|
||||
android_dx = join_paths(android_build_tools_dir, 'dx')
|
||||
android_zipalign = join_paths(android_build_tools_dir, 'zipalign')
|
||||
|
||||
javac = find_program('javac')
|
||||
jarsigner = find_program('jarsigner')
|
||||
rsvg_convert = find_program('rsvg-convert')
|
||||
convert = find_program('convert')
|
||||
zip = find_program('zip')
|
||||
android_gradlew = join_paths(meson.current_source_dir(), 'gradlew')
|
||||
|
||||
common_cppflags += '-I' + join_paths(meson.current_build_dir(), 'include')
|
||||
|
||||
#
|
||||
# AIDL
|
||||
#
|
||||
|
||||
IMainCallback_java = custom_target(
|
||||
'IMainCallback.java',
|
||||
output: 'IMainCallback.java',
|
||||
input: join_paths(meson.current_source_dir(), 'src', 'IMainCallback.aidl'),
|
||||
command: [
|
||||
join_paths(meson.current_source_dir(), 'run-aidl.sh'),
|
||||
android_aidl,
|
||||
'@INPUT@',
|
||||
'@OUTPUT@',
|
||||
join_paths(meson.current_build_dir(), 'src'),
|
||||
android_package_path,
|
||||
],
|
||||
)
|
||||
|
||||
IMain_java = custom_target(
|
||||
'IMain.java',
|
||||
output: 'IMain.java',
|
||||
input: join_paths(meson.current_source_dir(), 'src', 'IMain.aidl'),
|
||||
depends: IMainCallback_java,
|
||||
command: [
|
||||
join_paths(meson.current_source_dir(), 'run-aidl.sh'),
|
||||
android_aidl,
|
||||
'@INPUT@',
|
||||
'@OUTPUT@',
|
||||
join_paths(meson.current_build_dir(), 'src'),
|
||||
android_package_path,
|
||||
],
|
||||
)
|
||||
|
||||
#
|
||||
# Resources
|
||||
#
|
||||
|
||||
android_icon = custom_target(
|
||||
'Android icon',
|
||||
output: 'icon.png',
|
||||
input: '../mpd.svg',
|
||||
command: [
|
||||
rsvg_convert, '--width=48', '--height=48', '@INPUT@', '-o', '@OUTPUT@',
|
||||
],
|
||||
)
|
||||
|
||||
android_notification_icon = custom_target(
|
||||
'Android notification icon',
|
||||
output: 'notification_icon.png',
|
||||
input: android_icon,
|
||||
command: [
|
||||
convert, '@INPUT@', '-colorspace', 'Gray', '-gamma', '2.2', '@OUTPUT@',
|
||||
],
|
||||
)
|
||||
|
||||
resources_apk = custom_target(
|
||||
'resources.apk',
|
||||
output: ['resources.apk', 'R.java'],
|
||||
input: [
|
||||
'res/layout/custom_notification_gb.xml',
|
||||
'res/layout/log_item.xml',
|
||||
'res/layout/settings.xml',
|
||||
'res/values/strings.xml',
|
||||
android_icon,
|
||||
android_notification_icon,
|
||||
],
|
||||
command: [
|
||||
join_paths(meson.current_source_dir(), 'make-resources-apk.sh'),
|
||||
android_aapt,
|
||||
join_paths(android_sdk_platform_dir, 'android.jar'),
|
||||
android_package,
|
||||
android_package_path,
|
||||
'@OUTPUT0@',
|
||||
],
|
||||
)
|
||||
|
||||
#
|
||||
# Compile Java
|
||||
#
|
||||
|
||||
classes_jar = custom_target(
|
||||
'classes.jar',
|
||||
output: 'classes.jar',
|
||||
input: [
|
||||
'src/Bridge.java',
|
||||
'src/Loader.java',
|
||||
'src/Main.java',
|
||||
'src/Receiver.java',
|
||||
'src/Settings.java',
|
||||
IMain_java,
|
||||
IMainCallback_java,
|
||||
resources_apk[1],
|
||||
],
|
||||
command: [
|
||||
join_paths(meson.current_source_dir(), 'run-javac.sh'),
|
||||
javac,
|
||||
join_paths(android_sdk_platform_dir, 'android.jar'),
|
||||
android_package_path,
|
||||
zip,
|
||||
'@OUTPUT@',
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
||||
|
||||
classes_dex = custom_target(
|
||||
'classes.dex',
|
||||
output: 'classes.dex',
|
||||
input: classes_jar,
|
||||
command: [
|
||||
android_dx,
|
||||
'--dex', '--output', '@OUTPUT@',
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
||||
subdir('include')
|
||||
|
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:typeface="monospace" />
|
@@ -1,37 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/run"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textOn="@string/toggle_button_run_on"
|
||||
android:textOff="@string/toggle_button_run_off" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/run_on_boot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/checkbox_run_on_boot" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/wakelock"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/checkbox_wakelock" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/log_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="10dip" />
|
||||
|
||||
</LinearLayout>
|
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<string name="app_name">MPD</string>
|
||||
<string name="notification_title_mpd_running">Music Player Daemon is running</string>
|
||||
<string name="notification_text_mpd_running">Touch for MPD options.</string>
|
||||
<string name="toggle_button_run_on">MPD is running</string>
|
||||
<string name="toggle_button_run_off">MPD is not running</string>
|
||||
<string name="checkbox_run_on_boot">Run MPD automatically on boot</string>
|
||||
<string name="checkbox_wakelock">Prevent suspend when MPD is running (Wakelock)</string>
|
||||
</resources>
|
@@ -1,12 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
AIDL=$1
|
||||
SRC=$2
|
||||
DST=$3
|
||||
GENSRC=$4
|
||||
JAVA_PKG_PATH=$5
|
||||
|
||||
mkdir -p "$GENSRC/$JAVA_PKG_PATH"
|
||||
cp "$SRC" "$GENSRC/$JAVA_PKG_PATH/"
|
||||
"$AIDL" -I"$GENSRC" -o"$GENSRC" "$GENSRC/$JAVA_PKG_PATH/`basename $SRC`"
|
||||
exec cp "$GENSRC/$JAVA_PKG_PATH/`basename $DST`" "$DST"
|
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
JAVAC=$1
|
||||
CLASSPATH=$2
|
||||
JAVA_PKG_PATH=$3
|
||||
ZIP=$4
|
||||
JARFILE=`realpath "$5"`
|
||||
shift 5
|
||||
|
||||
D=`dirname "$JARFILE"`
|
||||
GENSRC="$D/src"
|
||||
GENCLASS="$D/classes"
|
||||
GENINCLUDE="$D/include"
|
||||
|
||||
mkdir -p "$GENSRC/$JAVA_PKG_PATH"
|
||||
"$JAVAC" -source 1.6 -target 1.6 -Xlint:-options \
|
||||
-cp "$CLASSPATH" \
|
||||
-h "$GENINCLUDE" \
|
||||
-d "$GENCLASS" \
|
||||
"$@"
|
||||
cd "$GENCLASS"
|
||||
zip -q -r "$JARFILE" .
|
18
android/settings.gradle.kts
Normal file
@@ -0,0 +1,18 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "MPD"
|
||||
include(":app")
|
||||
|
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
package org.musicpd;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Bridge to native code.
|
||||
*/
|
||||
public class Bridge {
|
||||
|
||||
/* used by jni */
|
||||
public interface LogListener {
|
||||
public void onLog(int priority, String msg);
|
||||
}
|
||||
|
||||
public static native void run(Context context, LogListener logListener);
|
||||
public static native void shutdown();
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
package org.musicpd;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class Loader {
|
||||
private static final String TAG = "MPD";
|
||||
|
||||
public static boolean loaded = false;
|
||||
public static String error;
|
||||
|
||||
static {
|
||||
try {
|
||||
System.loadLibrary("mpd");
|
||||
loaded = true;
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
error = e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,428 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
package org.musicpd;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class Main extends Service implements Runnable {
|
||||
private static final String TAG = "Main";
|
||||
private static final String REMOTE_ERROR = "MPD process was killed";
|
||||
private static final int MAIN_STATUS_ERROR = -1;
|
||||
private static final int MAIN_STATUS_STOPPED = 0;
|
||||
private static final int MAIN_STATUS_STARTED = 1;
|
||||
|
||||
private static final int MSG_SEND_STATUS = 0;
|
||||
private static final int MSG_SEND_LOG = 1;
|
||||
|
||||
private Thread mThread = null;
|
||||
private int mStatus = MAIN_STATUS_STOPPED;
|
||||
private boolean mAbort = false;
|
||||
private String mError = null;
|
||||
private final RemoteCallbackList<IMainCallback> mCallbacks = new RemoteCallbackList<IMainCallback>();
|
||||
private final IBinder mBinder = new MainStub(this);
|
||||
private PowerManager.WakeLock mWakelock = null;
|
||||
|
||||
static class MainStub extends IMain.Stub {
|
||||
private Main mService;
|
||||
MainStub(Main service) {
|
||||
mService = service;
|
||||
}
|
||||
public void start() {
|
||||
mService.start();
|
||||
}
|
||||
public void stop() {
|
||||
mService.stop();
|
||||
}
|
||||
public void setWakelockEnabled(boolean enabled) {
|
||||
mService.setWakelockEnabled(enabled);
|
||||
}
|
||||
public boolean isRunning() {
|
||||
return mService.isRunning();
|
||||
}
|
||||
public void registerCallback(IMainCallback cb) {
|
||||
mService.registerCallback(cb);
|
||||
}
|
||||
public void unregisterCallback(IMainCallback cb) {
|
||||
mService.unregisterCallback(cb);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void sendMessage(int what, int arg1, int arg2, Object obj) {
|
||||
int i = mCallbacks.beginBroadcast();
|
||||
while (i > 0) {
|
||||
i--;
|
||||
final IMainCallback cb = mCallbacks.getBroadcastItem(i);
|
||||
try {
|
||||
switch (what) {
|
||||
case MSG_SEND_STATUS:
|
||||
switch (arg1) {
|
||||
case MAIN_STATUS_ERROR:
|
||||
cb.onError((String)obj);
|
||||
break;
|
||||
case MAIN_STATUS_STOPPED:
|
||||
cb.onStopped();
|
||||
break;
|
||||
case MAIN_STATUS_STARTED:
|
||||
cb.onStarted();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MSG_SEND_LOG:
|
||||
cb.onLog(arg1, (String) obj);
|
||||
break;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
mCallbacks.finishBroadcast();
|
||||
}
|
||||
|
||||
private Bridge.LogListener mLogListener = new Bridge.LogListener() {
|
||||
@Override
|
||||
public void onLog(int priority, String msg) {
|
||||
sendMessage(MSG_SEND_LOG, priority, 0, msg);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
start();
|
||||
if (intent != null && intent.getBooleanExtra("wakelock", false))
|
||||
setWakelockEnabled(true);
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!Loader.loaded) {
|
||||
final String error = "Failed to load the native MPD libary.\n" +
|
||||
"Report this problem to us, and include the following information:\n" +
|
||||
"SUPPORTED_ABIS=" + String.join(", ", Build.SUPPORTED_ABIS) + "\n" +
|
||||
"PRODUCT=" + Build.PRODUCT + "\n" +
|
||||
"FINGERPRINT=" + Build.FINGERPRINT + "\n" +
|
||||
"error=" + Loader.error;
|
||||
setStatus(MAIN_STATUS_ERROR, error);
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (mAbort)
|
||||
return;
|
||||
setStatus(MAIN_STATUS_STARTED, null);
|
||||
}
|
||||
Bridge.run(this, mLogListener);
|
||||
setStatus(MAIN_STATUS_STOPPED, null);
|
||||
}
|
||||
|
||||
private synchronized void setStatus(int status, String error) {
|
||||
mStatus = status;
|
||||
mError = error;
|
||||
sendMessage(MSG_SEND_STATUS, mStatus, 0, mError);
|
||||
}
|
||||
|
||||
private Notification.Builder createNotificationBuilderWithChannel() {
|
||||
final NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (notificationManager == null)
|
||||
return null;
|
||||
|
||||
final String id = "org.musicpd";
|
||||
final String name = "MPD service";
|
||||
final int importance = 3; /* NotificationManager.IMPORTANCE_DEFAULT */
|
||||
|
||||
try {
|
||||
Class<?> ncClass = Class.forName("android.app.NotificationChannel");
|
||||
Constructor<?> ncCtor = ncClass.getConstructor(String.class, CharSequence.class, int.class);
|
||||
Object nc = ncCtor.newInstance(id, name, importance);
|
||||
|
||||
Method nmCreateNotificationChannelMethod =
|
||||
NotificationManager.class.getMethod("createNotificationChannel", ncClass);
|
||||
nmCreateNotificationChannelMethod.invoke(notificationManager, nc);
|
||||
|
||||
Constructor nbCtor = Notification.Builder.class.getConstructor(Context.class, String.class);
|
||||
return (Notification.Builder) nbCtor.newInstance(this, id);
|
||||
} catch (Exception e)
|
||||
{
|
||||
Log.e(TAG, "error creating the NotificationChannel", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void start() {
|
||||
if (mThread != null)
|
||||
return;
|
||||
|
||||
final Intent mainIntent = new Intent(this, Settings.class);
|
||||
mainIntent.setAction("android.intent.action.MAIN");
|
||||
mainIntent.addCategory("android.intent.category.LAUNCHER");
|
||||
final PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
||||
mainIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
Notification.Builder nBuilder;
|
||||
if (Build.VERSION.SDK_INT >= 26 /* Build.VERSION_CODES.O */)
|
||||
{
|
||||
nBuilder = createNotificationBuilderWithChannel();
|
||||
if (nBuilder == null)
|
||||
return;
|
||||
}
|
||||
else
|
||||
nBuilder = new Notification.Builder(this);
|
||||
|
||||
Notification notification = nBuilder.setContentTitle(getText(R.string.notification_title_mpd_running))
|
||||
.setContentText(getText(R.string.notification_text_mpd_running))
|
||||
.setSmallIcon(R.drawable.notification_icon)
|
||||
.setContentIntent(contentIntent)
|
||||
.build();
|
||||
|
||||
mThread = new Thread(this);
|
||||
mThread.start();
|
||||
|
||||
startForeground(R.string.notification_title_mpd_running, notification);
|
||||
startService(new Intent(this, Main.class));
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
if (mThread != null) {
|
||||
if (mThread.isAlive()) {
|
||||
synchronized (this) {
|
||||
if (mStatus == MAIN_STATUS_STARTED)
|
||||
Bridge.shutdown();
|
||||
else
|
||||
mAbort = true;
|
||||
}
|
||||
}
|
||||
try {
|
||||
mThread.join();
|
||||
mThread = null;
|
||||
mAbort = false;
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
setWakelockEnabled(false);
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
private void setWakelockEnabled(boolean enabled) {
|
||||
if (enabled && mWakelock == null) {
|
||||
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
|
||||
mWakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
|
||||
mWakelock.acquire();
|
||||
Log.d(TAG, "Wakelock acquired");
|
||||
} else if (!enabled && mWakelock != null) {
|
||||
mWakelock.release();
|
||||
mWakelock = null;
|
||||
Log.d(TAG, "Wakelock released");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRunning() {
|
||||
return mThread != null && mThread.isAlive();
|
||||
}
|
||||
|
||||
private void registerCallback(IMainCallback cb) {
|
||||
if (cb != null) {
|
||||
mCallbacks.register(cb);
|
||||
sendMessage(MSG_SEND_STATUS, mStatus, 0, mError);
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterCallback(IMainCallback cb) {
|
||||
if (cb != null) {
|
||||
mCallbacks.unregister(cb);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Client that bind the Main Service in order to send commands and receive callback
|
||||
*/
|
||||
public static class Client {
|
||||
|
||||
public interface Callback {
|
||||
public void onStarted();
|
||||
public void onStopped();
|
||||
public void onError(String error);
|
||||
public void onLog(int priority, String msg);
|
||||
}
|
||||
|
||||
private boolean mBound = false;
|
||||
private final Context mContext;
|
||||
private Callback mCallback;
|
||||
private IMain mIMain = null;
|
||||
|
||||
private final IMainCallback.Stub mICallback = new IMainCallback.Stub() {
|
||||
|
||||
@Override
|
||||
public void onStopped() throws RemoteException {
|
||||
mCallback.onStopped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStarted() throws RemoteException {
|
||||
mCallback.onStarted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) throws RemoteException {
|
||||
mCallback.onError(error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLog(int priority, String msg) throws RemoteException {
|
||||
mCallback.onLog(priority, msg);
|
||||
}
|
||||
};
|
||||
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
synchronized (this) {
|
||||
mIMain = IMain.Stub.asInterface(service);
|
||||
try {
|
||||
if (mCallback != null)
|
||||
mIMain.registerCallback(mICallback);
|
||||
} catch (RemoteException e) {
|
||||
if (mCallback != null)
|
||||
mCallback.onError(REMOTE_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
if (mCallback != null)
|
||||
mCallback.onError(REMOTE_ERROR);
|
||||
}
|
||||
};
|
||||
|
||||
public Client(Context context, Callback cb) throws IllegalArgumentException {
|
||||
if (context == null)
|
||||
throw new IllegalArgumentException("Context can't be null");
|
||||
mContext = context;
|
||||
mCallback = cb;
|
||||
mBound = mContext.bindService(new Intent(mContext, Main.class), mServiceConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
public boolean start() {
|
||||
synchronized (this) {
|
||||
if (mIMain != null) {
|
||||
try {
|
||||
mIMain.start();
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean stop() {
|
||||
synchronized (this) {
|
||||
if (mIMain != null) {
|
||||
try {
|
||||
mIMain.stop();
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setWakelockEnabled(boolean enabled) {
|
||||
synchronized (this) {
|
||||
if (mIMain != null) {
|
||||
try {
|
||||
mIMain.setWakelockEnabled(enabled);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
synchronized (this) {
|
||||
if (mIMain != null) {
|
||||
try {
|
||||
return mIMain.isRunning();
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
if (mBound) {
|
||||
synchronized (this) {
|
||||
if (mIMain != null && mICallback != null) {
|
||||
try {
|
||||
if (mCallback != null)
|
||||
mIMain.unregisterCallback(mICallback);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
mBound = false;
|
||||
mContext.unbindService(mServiceConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* start Main service without any callback
|
||||
*/
|
||||
public static void start(Context context, boolean wakelock) {
|
||||
Intent intent = new Intent(context, Main.class)
|
||||
.putExtra("wakelock", wakelock);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
/* in Android 8+, we need to use this method
|
||||
or else we'll get "IllegalStateException:
|
||||
app is in background" */
|
||||
context.startForegroundService(intent);
|
||||
else
|
||||
context.startService(intent);
|
||||
}
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
package org.musicpd;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
public class Receiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d("Receiver", "onReceive: " + intent);
|
||||
if (intent.getAction() == "android.intent.action.BOOT_COMPLETED") {
|
||||
if (Settings.Preferences.getBoolean(context,
|
||||
Settings.Preferences.KEY_RUN_ON_BOOT, false)) {
|
||||
final boolean wakelock = Settings.Preferences.getBoolean(context,
|
||||
Settings.Preferences.KEY_WAKELOCK, false);
|
||||
Main.start(context, wakelock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,290 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2021 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
package org.musicpd;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ToggleButton;
|
||||
|
||||
public class Settings extends Activity {
|
||||
private static final String TAG = "Settings";
|
||||
private Main.Client mClient;
|
||||
private TextView mTextStatus;
|
||||
private ToggleButton mRunButton;
|
||||
private boolean mFirstRun;
|
||||
private LinkedList<String> mLogListArray = new LinkedList<String>();
|
||||
private ListView mLogListView;
|
||||
private ArrayAdapter<String> mLogListAdapter;
|
||||
|
||||
private static final int MAX_LOGS = 500;
|
||||
|
||||
private static final int MSG_ERROR = 0;
|
||||
private static final int MSG_STOPPED = 1;
|
||||
private static final int MSG_STARTED = 2;
|
||||
private static final int MSG_LOG = 3;
|
||||
|
||||
public static class Preferences {
|
||||
public static final String KEY_RUN_ON_BOOT ="run_on_boot";
|
||||
public static final String KEY_WAKELOCK ="wakelock";
|
||||
|
||||
public static SharedPreferences get(Context context) {
|
||||
return context.getSharedPreferences(TAG, MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public static void putBoolean(Context context, String key, boolean value) {
|
||||
final SharedPreferences prefs = get(context);
|
||||
|
||||
if (prefs == null)
|
||||
return;
|
||||
final Editor editor = prefs.edit();
|
||||
editor.putBoolean(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static boolean getBoolean(Context context, String key, boolean defValue) {
|
||||
final SharedPreferences prefs = get(context);
|
||||
|
||||
return prefs != null ? prefs.getBoolean(key, defValue) : defValue;
|
||||
}
|
||||
}
|
||||
|
||||
private Handler mHandler = new Handler(new Handler.Callback() {
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_ERROR:
|
||||
Log.d(TAG, "onError");
|
||||
|
||||
mClient.release();
|
||||
connectClient();
|
||||
|
||||
mRunButton.setEnabled(false);
|
||||
mRunButton.setChecked(false);
|
||||
|
||||
mTextStatus.setText((String)msg.obj);
|
||||
mFirstRun = true;
|
||||
break;
|
||||
case MSG_STOPPED:
|
||||
Log.d(TAG, "onStopped");
|
||||
mRunButton.setEnabled(true);
|
||||
if (!mFirstRun && Preferences.getBoolean(Settings.this, Preferences.KEY_RUN_ON_BOOT, false))
|
||||
mRunButton.setChecked(true);
|
||||
else
|
||||
mRunButton.setChecked(false);
|
||||
mFirstRun = true;
|
||||
mTextStatus.setText("");
|
||||
break;
|
||||
case MSG_STARTED:
|
||||
Log.d(TAG, "onStarted");
|
||||
mRunButton.setChecked(true);
|
||||
mFirstRun = true;
|
||||
mTextStatus.setText("MPD service started");
|
||||
break;
|
||||
case MSG_LOG:
|
||||
if (mLogListArray.size() > MAX_LOGS)
|
||||
mLogListArray.remove(0);
|
||||
String priority;
|
||||
switch (msg.arg1) {
|
||||
case Log.DEBUG:
|
||||
priority = "D";
|
||||
break;
|
||||
case Log.ERROR:
|
||||
priority = "E";
|
||||
break;
|
||||
case Log.INFO:
|
||||
priority = "I";
|
||||
break;
|
||||
case Log.VERBOSE:
|
||||
priority = "V";
|
||||
break;
|
||||
case Log.WARN:
|
||||
priority = "W";
|
||||
break;
|
||||
default:
|
||||
priority = "";
|
||||
}
|
||||
mLogListArray.add(priority + "/ " + (String)msg.obj);
|
||||
mLogListAdapter.notifyDataSetChanged();
|
||||
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
private final OnCheckedChangeListener mOnRunChangeListener = new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
if (mClient != null) {
|
||||
if (isChecked) {
|
||||
mClient.start();
|
||||
if (Preferences.getBoolean(Settings.this,
|
||||
Preferences.KEY_WAKELOCK, false))
|
||||
mClient.setWakelockEnabled(true);
|
||||
} else {
|
||||
mClient.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final OnCheckedChangeListener mOnRunOnBootChangeListener = new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Preferences.putBoolean(Settings.this, Preferences.KEY_RUN_ON_BOOT, isChecked);
|
||||
if (isChecked && mClient != null && !mRunButton.isChecked())
|
||||
mRunButton.setChecked(true);
|
||||
}
|
||||
};
|
||||
|
||||
private final OnCheckedChangeListener mOnWakelockChangeListener = new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Preferences.putBoolean(Settings.this, Preferences.KEY_WAKELOCK, isChecked);
|
||||
if (mClient != null && mClient.isRunning())
|
||||
mClient.setWakelockEnabled(isChecked);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
/* TODO: this sure is the wrong place to request
|
||||
permissions - it will cause MPD to quit
|
||||
immediately; we should request permissions when we
|
||||
need them, but implementing that is complicated, so
|
||||
for now, we do it here to give users a quick
|
||||
solution for the problem */
|
||||
requestAllPermissions();
|
||||
|
||||
setContentView(R.layout.settings);
|
||||
mRunButton = (ToggleButton) findViewById(R.id.run);
|
||||
mRunButton.setOnCheckedChangeListener(mOnRunChangeListener);
|
||||
|
||||
mTextStatus = (TextView) findViewById(R.id.status);
|
||||
|
||||
mLogListAdapter = new ArrayAdapter<String>(this, R.layout.log_item, mLogListArray);
|
||||
|
||||
mLogListView = (ListView) findViewById(R.id.log_list);
|
||||
mLogListView.setAdapter(mLogListAdapter);
|
||||
mLogListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
|
||||
|
||||
CheckBox checkbox = (CheckBox) findViewById(R.id.run_on_boot);
|
||||
checkbox.setOnCheckedChangeListener(mOnRunOnBootChangeListener);
|
||||
if (Preferences.getBoolean(this, Preferences.KEY_RUN_ON_BOOT, false))
|
||||
checkbox.setChecked(true);
|
||||
|
||||
checkbox = (CheckBox) findViewById(R.id.wakelock);
|
||||
checkbox.setOnCheckedChangeListener(mOnWakelockChangeListener);
|
||||
if (Preferences.getBoolean(this, Preferences.KEY_WAKELOCK, false))
|
||||
checkbox.setChecked(true);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
private void checkRequestPermission(String permission) {
|
||||
if (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED)
|
||||
return;
|
||||
|
||||
try {
|
||||
this.requestPermissions(new String[]{permission}, 0);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "requestPermissions(" + permission + ") failed",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
private void requestAllPermissions() {
|
||||
if (android.os.Build.VERSION.SDK_INT < 23)
|
||||
/* we don't need to request permissions on
|
||||
this old Android version */
|
||||
return;
|
||||
|
||||
/* starting with Android 6.0, we need to explicitly
|
||||
request all permissions before using them;
|
||||
mentioning them in the manifest is not enough */
|
||||
|
||||
checkRequestPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
private void connectClient() {
|
||||
mClient = new Main.Client(this, new Main.Client.Callback() {
|
||||
|
||||
private void removeMessages() {
|
||||
/* don't remove log messages */
|
||||
mHandler.removeMessages(MSG_STOPPED);
|
||||
mHandler.removeMessages(MSG_STARTED);
|
||||
mHandler.removeMessages(MSG_ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopped() {
|
||||
removeMessages();
|
||||
mHandler.sendEmptyMessage(MSG_STOPPED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStarted() {
|
||||
removeMessages();
|
||||
mHandler.sendEmptyMessage(MSG_STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) {
|
||||
removeMessages();
|
||||
mHandler.sendMessage(Message.obtain(mHandler, MSG_ERROR, error));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLog(int priority, String msg) {
|
||||
mHandler.sendMessage(Message.obtain(mHandler, MSG_LOG, priority, 0, msg));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
mFirstRun = false;
|
||||
connectClient();
|
||||
super.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
mClient.release();
|
||||
mClient = null;
|
||||
super.onStop();
|
||||
}
|
||||
}
|
@@ -10,4 +10,4 @@ ROOT=`dirname "$BIN"`
|
||||
export PKG_CONFIG_DIR=
|
||||
export PKG_CONFIG_LIBDIR="${ROOT}/lib/pkgconfig:${ROOT}/share/pkgconfig"
|
||||
|
||||
exec /usr/bin/pkg-config "$@"
|
||||
exec pkg-config "$@"
|
||||
|
3
doc/_static/css/custom.css
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/*
|
||||
* css to override sphinx theme
|
||||
*/
|