Compare commits
1085 Commits
v1.8.9
...
a5e3e9b3cf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5e3e9b3cf | ||
|
248ba50a8e
|
|||
|
|
45a82b3c2d
|
||
|
770d1293dc
|
|||
|
d10538e720
|
|||
|
02e9e2384e
|
|||
|
|
b627ff4413
|
||
|
|
0683ea43f4
|
||
|
08f77a5cae
|
|||
|
27df792775
|
|||
|
|
800f92f255
|
||
|
|
e06ed3ed7d | ||
|
|
3bb119c6ed | ||
|
|
0762699a86 | ||
|
|
8837326d44 | ||
|
|
481a3d5784 | ||
|
8eff782f54
|
|||
|
c590734c42
|
|||
|
7e7f68282b
|
|||
|
4bd5cee17b
|
|||
|
b2421dc8b1
|
|||
|
93363bff96
|
|||
|
d01044b203
|
|||
|
70fe513e52
|
|||
|
dee93fde8f
|
|||
|
f0ce05e06e
|
|||
|
4975ba2752
|
|||
|
ed8c60e52f
|
|||
|
|
7735247521 | ||
|
|
b32a6ad1b8 | ||
|
|
21a512ae71 | ||
|
|
b0944d3f1c | ||
|
|
8a9b719c50 | ||
|
|
ef7454ae75 | ||
|
|
b51f6abfce | ||
|
|
a013635224 | ||
|
|
a9f028a34b | ||
|
|
3b0fba12f5 | ||
|
|
4139f09fb7 | ||
|
|
248ff2ec62 | ||
|
|
cc7178eb21 | ||
|
6116ef9450
|
|||
|
8801960615
|
|||
|
b0a4284b66
|
|||
|
|
334dabb407 | ||
|
|
b2fc79a3ab | ||
|
|
f0e8643e6b | ||
|
|
5cd616f967 | ||
|
|
b53ef758ec | ||
|
|
5add03bf23 | ||
|
|
b465ee588b | ||
|
|
b9253d41ea | ||
|
|
ca0a9dd85f | ||
|
|
9951525cbe | ||
|
|
98b2c9cce2 | ||
|
|
19de2a2d1c | ||
|
|
80f783b669 | ||
|
|
761fe59c5e | ||
|
|
f58d8a52ff | ||
|
|
f4e5b68258 | ||
|
|
5df3ee0d61 | ||
|
|
9f66a27b82 | ||
|
|
4bbb20e18e | ||
|
|
4f9ab6d263 | ||
|
|
af21fd25db | ||
|
|
35097e1263 | ||
|
|
8e4274d923 | ||
|
|
12649141b1 | ||
|
|
0526d37fbd | ||
|
|
b083899ec8 | ||
|
|
6b793c7c16 | ||
|
|
11ca993a75 | ||
|
|
7348ddeffa | ||
|
88df8d2552
|
|||
|
d4f4f8b4a9
|
|||
|
9ca1aa911a
|
|||
|
ba57ebad31
|
|||
|
|
e4d2c93a1d | ||
|
|
07065a60ad | ||
|
|
916cd76be1 | ||
|
8b55ffb147
|
|||
|
727e88b7b1
|
|||
|
f70d33878c
|
|||
|
6a55d3c01a
|
|||
|
69b5ed3a6d
|
|||
|
8b2adbf301
|
|||
|
88cc3a2a12
|
|||
|
26526a71a9
|
|||
|
11eb3713e5
|
|||
|
1df9cc7664
|
|||
|
|
499eb467cb | ||
|
|
9d4df73c56 | ||
|
|
d0f32dfa0a | ||
|
|
c609eceefc | ||
|
|
eb68220b8d | ||
|
|
f1a003fabf | ||
|
b76da35752
|
|||
|
abead88ceb
|
|||
|
908eb4ac99
|
|||
|
|
856a0726f7 | ||
|
|
df97b1fd4f | ||
|
71a05d598a
|
|||
|
2131fb3a3d
|
|||
|
1470dcd560
|
|||
| dc678e59df | |||
|
|
d95084500f | ||
|
6f7215b46f
|
|||
|
|
c423d2b3ca | ||
|
|
758fc56277 | ||
|
|
c7d1e92cae | ||
|
|
d24dcbf2ca | ||
|
|
579b33cc78 | ||
|
|
1a3e6315a1 | ||
|
|
7f4d06d15d | ||
|
|
b8033a6012 | ||
|
471f977209
|
|||
|
2ba5f5fb98
|
|||
|
4de67bd27a
|
|||
|
0bb82a48a5
|
|||
|
d5a5273607
|
|||
|
404671546c
|
|||
|
a94c6e8b9f
|
|||
|
da09df2dd1
|
|||
|
40a4d254f5
|
|||
|
177d01edd9
|
|||
|
9f5da7b4fe
|
|||
|
e42d41ef30
|
|||
|
5498ff8a87
|
|||
|
20dcc7929b
|
|||
|
1283cf979d
|
|||
|
0a493ff065
|
|||
|
16cd249bae
|
|||
|
d136572960
|
|||
|
318aa3c821
|
|||
|
73712e0d10
|
|||
| bf504641c6 | |||
|
ec1f0c5356
|
|||
|
2154d8c192
|
|||
|
961fb7618f
|
|||
|
11f726064a
|
|||
|
4c5a722dc5
|
|||
| ee39ff0eda | |||
| abced92a07 | |||
|
|
eeacc5eef8 | ||
|
|
16c65f19fd | ||
|
|
6a9c3a36eb | ||
|
|
99ccb44ad3 | ||
| fc402fd81b | |||
| f7c6bc716c | |||
| 2535347d5a | |||
|
|
8c0655c352 | ||
|
|
335f8c32fd | ||
|
|
f77c064722 | ||
|
|
8b0b757f04 | ||
| 13af054214 | |||
| fcbb586e80 | |||
| 458c9f3b15 | |||
| dfa74960b3 | |||
| f082842fbe | |||
| d4d818a085 | |||
|
|
f074d0331d | ||
|
|
f94bc62a94 | ||
|
|
428bf42e79 | ||
| b0097fa45e | |||
|
|
15fa1fea8c | ||
| 31368cff45 | |||
| b44dd96dd3 | |||
| 838e13f30c | |||
|
|
08fab0cd8d | ||
|
|
a142685d2e | ||
| a7a95f2030 | |||
|
|
d3c90481be | ||
| f9bb0d7d7b | |||
| f23a1bedee | |||
| dcf31ba115 | |||
| 0eae43fe55 | |||
| c0fcd4e763 | |||
| 79e2402c9d | |||
|
|
5409a324e8 | ||
|
|
504362e680 | ||
|
|
0ce2f8fae2 | ||
| 2fc6c67432 | |||
| 459cf02642 | |||
| 44cb64601f | |||
| 53c270ee12 | |||
| e4b16f6d59 | |||
| 3021e408a7 | |||
| 7649119db7 | |||
| b0a4f26e7a | |||
|
|
dd47d4cf08 | ||
|
|
8912a9d73c | ||
|
|
9bd1c6f685 | ||
|
|
e15e06034c | ||
|
|
a6180a5049 | ||
|
|
7c69b17e77 | ||
|
|
602a40a532 | ||
|
|
e00998f913 | ||
|
|
af5914c2db | ||
|
|
6c5f70638a | ||
|
|
ba88552919 | ||
|
|
8c90c4f68d | ||
|
|
08eb1a600d | ||
|
|
8ea0480d4a | ||
|
|
36ee39589e | ||
|
|
ac7a721940 | ||
|
|
b36ca8673a | ||
|
|
7cfac486f9 | ||
|
|
21dd413ab5 | ||
|
|
39e10638b5 | ||
| 0a95b6d2a0 | |||
| e6bcbb39ff | |||
| 90de1f3723 | |||
| 68740b4043 | |||
| b66a6ff717 | |||
| 7c1a91e35a | |||
| 366e9af167 | |||
| 03b02aaa06 | |||
|
|
180cf25ad3 | ||
| 3490cd183a | |||
|
|
0c292d1eaa | ||
| 0b256cb2c0 | |||
| cc03c48648 | |||
| 53e7ef2918 | |||
| 9609bee0f7 | |||
| 5b23a3f960 | |||
| d2d45991b0 | |||
| 25aeafb759 | |||
| 9045ae5c24 | |||
| 555873354c | |||
| 2a012619a7 | |||
| d1de3a852b | |||
| f04e37e905 | |||
| 825a5f2cb9 | |||
| 0de6896691 | |||
| 7187214282 | |||
|
|
9d4161a255 | ||
|
|
d82a3c145d | ||
|
|
a6cbd44e42 | ||
|
|
45c62b4ab2 | ||
|
|
d53312d261 | ||
|
|
c453810785 | ||
|
|
999057300b | ||
|
|
2245347e28 | ||
|
|
b79f7850ed | ||
|
|
4469f86e46 | ||
|
|
bdeddb149e | ||
|
|
3b12ec4bfe | ||
| ca4618579e | |||
| 2f13aba22c | |||
| dba5912ecd | |||
| 88b8663484 | |||
| 609886fd09 | |||
| a4db997e06 | |||
|
|
090216ccb7 | ||
|
|
472092d1bf | ||
|
|
6d3791de31 | ||
|
|
24a99d5791 | ||
|
|
6c631b1cf6 | ||
|
|
5d7677b354 | ||
|
|
22820f4f24 | ||
| 1d3bd48535 | |||
|
|
694446b7e3 | ||
|
|
20ae9d247e | ||
|
|
1943fac610 | ||
|
|
a07a50e635 | ||
|
|
8ccb9bbab1 | ||
|
|
743b8d26ad | ||
|
|
c47bd4c328 | ||
|
|
d84abc3a6a | ||
|
|
4ae85f1ec0 | ||
|
|
79d40d1d79 | ||
|
|
1902b8821e | ||
|
|
075542d605 | ||
|
|
0b5894ee6d | ||
|
|
92fb9dbdb6 | ||
|
|
1860abf532 | ||
|
|
3f4780c9fb | ||
|
|
d5d6e4616e | ||
|
|
18e267053d | ||
| 7ba9a7e4e7 | |||
| 5b8a7c39e2 | |||
| d40a5a89cd | |||
| 308d558347 | |||
| 9770ce187a | |||
| 535bc03b70 | |||
| 642e45af89 | |||
| 56d2307b75 | |||
| d875af8a8e | |||
| feeb4f057d | |||
| 1ad5c6b896 | |||
| baee3b9f86 | |||
| e6167baab1 | |||
|
|
1c15e7742e | ||
|
|
072ba63789 | ||
|
|
0fa0daa058 | ||
|
|
5a5ed3d631 | ||
|
|
3dfa376f59 | ||
|
|
7bf74634bb | ||
|
|
fc645a81bc | ||
|
|
35365bbdf3 | ||
|
|
779ef5dbee | ||
|
|
e82994c76b | ||
|
|
4e1d01d8d1 | ||
|
|
5de0fc86e5 | ||
| e26b643423 | |||
| 621534d610 | |||
| a01300e9c6 | |||
| ecb8ce105a | |||
| 32ef3c14f7 | |||
| 4972257635 | |||
| c98cb50baa | |||
| c331f34fa9 | |||
| a1aea532b5 | |||
| 43489aeb4c | |||
| 990c85aedd | |||
|
|
64337b9bee | ||
|
|
8bdfaa2434 | ||
|
|
5f6060858d | ||
| b62e436054 | |||
| bf63b4dbcf | |||
| a82d940bcc | |||
| ba59dc7ca9 | |||
| 181290a0f3 | |||
|
|
d553c2f3f2 | ||
|
|
7776093217 | ||
|
|
b27f3f8540 | ||
| eb041bf6b2 | |||
|
|
e3c53bf07f | ||
|
|
9ddab6ee59 | ||
|
|
2615795402 | ||
|
|
6531445d7f | ||
|
|
4fbf8a8ca2 | ||
|
|
707b2b4380 | ||
|
|
aae85c1170 | ||
|
|
c12a6c6a4d | ||
|
|
b15c02adbf | ||
|
|
9e24128675 | ||
|
|
66c61e2e6c | ||
|
|
7bddfbe5a7 | ||
|
|
8036b10ee6 | ||
|
|
71f400f587 | ||
|
|
79e302f922 | ||
|
|
af7f60fc4d | ||
|
|
7cc4b66dfd | ||
|
|
a9fddf9963 | ||
|
|
36c1504c6a | ||
|
|
d644170141 | ||
|
|
d38f83e961 | ||
|
|
c50c5af497 | ||
|
|
fa3774a32b | ||
|
|
fd124f2a6c | ||
|
|
265b65eb8a | ||
|
|
4c269c55d2 | ||
|
|
c03305120e | ||
|
|
29615b670b | ||
|
|
6ab4a696b6 | ||
|
|
23479c7765 | ||
|
|
6d98f7aafa | ||
| 75078ed52b | |||
| 2a0afedb1d | |||
| 66a2b41250 | |||
|
|
d6a7fa3d7a | ||
|
|
07e55f1c76 | ||
| 4ee5dd910b | |||
| 87f071b5b4 | |||
| bb0b5e8adf | |||
|
|
c79d1e82a5 | ||
|
|
4aebeedec6 | ||
|
|
7de94f2caf | ||
|
|
17ed85fc1b | ||
|
|
4355fb4d68 | ||
|
|
508200abeb | ||
|
|
a29943e783 | ||
|
|
3e6a9181d6 | ||
| 1fe3a3d1ca | |||
|
|
b2951a3475 | ||
|
|
9d3c63cf62 | ||
|
|
65d237254c | ||
|
|
fe1d5c66cb | ||
|
|
113a5028af | ||
|
|
1a56260757 | ||
|
|
697fffbc99 | ||
|
|
804edfa64e | ||
| 2ab6c396d0 | |||
| a55f467339 | |||
|
|
cf682f68c9 | ||
| 0e988e746c | |||
|
|
f119cbf8e7 | ||
|
|
056f5f6fce | ||
|
|
42f6125d5e | ||
|
|
3e20fc4d1d | ||
|
|
1f763feb69 | ||
|
|
6e7ad329fe | ||
|
|
5cb241475d | ||
|
|
27e76c7243 | ||
|
|
576ad04064 | ||
|
|
5f8187ef6d | ||
|
|
f16f919e27 | ||
|
|
736bb8a75e | ||
|
|
de9ad6d4a4 | ||
|
|
1d37ce54ea | ||
|
|
f88f1cfb54 | ||
|
|
fc1478645b | ||
|
|
ffab001b09 | ||
|
|
a58a8005e1 | ||
|
|
c884ada187 | ||
|
|
e3d46ad5a0 | ||
|
|
f4a2c03216 | ||
|
|
f2b8f2f98d | ||
|
|
2c5fd87a2a | ||
|
|
39768f7f04 | ||
|
|
cc3e1ced15 | ||
|
|
2e26cc104e | ||
|
|
42fd0926ef | ||
| ec202aa9a7 | |||
|
|
1fb56c8777 | ||
|
|
a5d4a37da8 | ||
|
|
4804a48549 | ||
|
|
c892a845b4 | ||
|
|
e98064b6a5 | ||
| 57f5f6ed5b | |||
| 79f5b8b7e8 | |||
| f15c660d33 | |||
| 1866743c47 | |||
| 6b9a7917b4 | |||
| 2a5725f382 | |||
| edeabb6ee3 | |||
| 24a9fbe414 | |||
| e5b8c4c3c4 | |||
|
|
a781a1f947 | ||
|
|
13e57b5026 | ||
|
|
f8c7abfff4 | ||
|
|
7fe3ce970c | ||
| 9c395243f4 | |||
|
|
b9eb244b0b | ||
|
|
420a99f1cf | ||
| e756a639ae | |||
|
|
91ff5f7a0c | ||
|
|
67b55a4ecf | ||
|
|
a5ae2eaa63 | ||
|
|
0bc2a8b6d4 | ||
|
|
95a1786c4a | ||
|
|
caa1c9d72e | ||
|
|
a7afe0b309 | ||
|
|
9c03174eef | ||
|
|
da02926fa6 | ||
|
|
250dabfe58 | ||
|
|
0bba3b76bc | ||
|
|
c2479278ba | ||
|
|
499a403a06 | ||
|
|
af5d622339 | ||
|
|
5eeb54bc47 | ||
|
|
e09e899aad | ||
| 69f0fc6c3a | |||
| 705dfb9cbd | |||
| d0ef749f19 | |||
| b54711243f | |||
| 37f03aca37 | |||
|
|
271de59a94 | ||
|
|
0ab55f6f5a | ||
|
|
43921721d4 | ||
|
|
33c88cded3 | ||
|
|
aecce891ea | ||
|
|
2ea98a7756 | ||
|
|
e667872d83 | ||
|
|
3602a614c4 | ||
|
|
d8c5f4d93c | ||
|
|
c9f4df9dae | ||
|
|
feb384bca6 | ||
|
|
71e9160460 | ||
|
|
64966d3c86 | ||
|
|
4787df4074 | ||
| 92291fd919 | |||
| be51538704 | |||
| 89b24911ba | |||
| 1cf71b3973 | |||
| 2fe3b15806 | |||
| 33468bfc1c | |||
|
|
5908692a5c | ||
|
|
693dce8719 | ||
| b232827dfd | |||
|
|
02f9f411ce | ||
| bbf9da44e1 | |||
|
|
ee896fb4f9 | ||
|
|
4d7d8b6206 | ||
|
|
55b841a8b4 | ||
| 7fac86b617 | |||
|
|
c31d42be2d | ||
| 31c09b9c0b | |||
|
|
db91dce57f | ||
| 12c76245e6 | |||
| 2163a2b93b | |||
| afad56ab91 | |||
| ece1b93f8d | |||
| 10416e40fa | |||
| 88f8581acc | |||
| b33dd2a994 | |||
| d87961d800 | |||
| 38d2606d6d | |||
| 0a91c097e8 | |||
|
|
b1c53bd820 | ||
| c973f93424 | |||
| fcadbe7c38 | |||
| 59a4d7552c | |||
| 9d7840bdd1 | |||
| 89e3dd7655 | |||
| df56608a5d | |||
| 3489f176d6 | |||
| 75fdd9d2b1 | |||
| dd1999681f | |||
|
|
32db4e363b | ||
|
|
004bb8d71c | ||
|
|
21a1e88c47 | ||
|
|
86fb718896 | ||
|
|
a4e9b2f874 | ||
|
|
ac924470b8 | ||
| 63883032c4 | |||
| a8c9b1b71c | |||
| 61529fde4d | |||
| 1050cc4e47 | |||
| a7845c9290 | |||
| 1feefe107c | |||
| 1654ff9af3 | |||
| 58adff6a22 | |||
| 2728c311d8 | |||
| b561dfe90d | |||
| 53a40b2cfa | |||
| 98ce802f87 | |||
| 853b4275be | |||
| df4355c072 | |||
| 33c3feb11a | |||
| f88a9bd46a | |||
| 5dd9f43464 | |||
| 4a13623b2c | |||
| 6b368bffdf | |||
| 8d87988871 | |||
| 256ca13dca | |||
| b0c5552cfa | |||
| 616ab4861d | |||
| fcff067b2e | |||
| da018fc64d | |||
| f27a9f9103 | |||
|
|
fe59565ded | ||
|
|
419d2faa78 | ||
|
|
55f3bab13a | ||
|
|
212cc4259b | ||
|
|
e1ec8cfa10 | ||
|
|
6af2edb690 | ||
|
|
9d6b3ebb25 | ||
|
|
ed0744d5ab | ||
|
|
b59886dc77 | ||
|
|
23954eed27 | ||
| 071b6bd090 | |||
| 298bf1a538 | |||
| 8b2285787c | |||
| c83cd5fd10 | |||
| 381c67d020 | |||
| 81cffc7253 | |||
| e745b959ca | |||
|
|
d2ee210376 | ||
|
|
9c81f22a4d | ||
| 51947576aa | |||
|
|
6411f65fa5 | ||
|
|
a04349bfaa | ||
| 7b68938e14 | |||
| ee462715de | |||
| a95da72e19 | |||
| 3ece80f3da | |||
|
|
e00ff99af7 | ||
|
|
fc26d7967f | ||
| 9b753b07f8 | |||
|
|
73deb535ef | ||
|
|
a733e6ddf0 | ||
|
|
86c7200e6b | ||
|
|
15282e7ea6 | ||
|
|
96f87df052 | ||
|
|
bc623f044d | ||
|
|
773eef26e7 | ||
| b58af03a7c | |||
| c53997ffcc | |||
| 11d55bbf02 | |||
|
|
50ebff3edf | ||
| 8e1aa3d01e | |||
|
|
ea66cf04b3 | ||
|
|
b1da72dc3a | ||
|
|
91bd17bb9e | ||
| fca9557108 | |||
|
|
d76a4a84d0 | ||
|
|
8790c0e87b | ||
|
|
bc461e1239 | ||
| 589fd2f0f1 | |||
|
|
2e94bebcac | ||
|
|
ff748b27a5 | ||
|
|
8522318ed1 | ||
|
|
1f08d2f71c | ||
|
|
7fb3489ca8 | ||
| 129c86a030 | |||
| e4121b2564 | |||
| 98abebe099 | |||
| 0f86cb4d53 | |||
|
|
4dd77463fb | ||
|
|
2cf3347f8c | ||
| 5516f40571 | |||
| c006659ba4 | |||
| 5be2c51d79 | |||
|
|
2dcce080d3 | ||
|
|
6c3b4135c2 | ||
| 48c1adb3bb | |||
| 89bde4c9ae | |||
| a7e2e0e8da | |||
| 64d77fbb6b | |||
|
|
4b80e60b2b | ||
|
|
0f80505735 | ||
|
|
c26c140310 | ||
|
|
c05e008569 | ||
| a5255690ed | |||
| 56f0bbb7bd | |||
| 4e72bf93f8 | |||
| d31e42446e | |||
|
|
8ea71dc2b5 | ||
| 3ba26ac67e | |||
|
|
8537e0f910 | ||
| a6c9ea22e6 | |||
| 5767817de2 | |||
| b80344a443 | |||
| f36bc788c7 | |||
|
|
22136774de | ||
|
|
be39237a22 | ||
|
|
836eebd20b | ||
|
|
8d2aebaf7f | ||
|
|
1c2efb6c22 | ||
|
|
784877871a | ||
|
|
4a2fb2ac47 | ||
|
|
7caf22bd7a | ||
|
|
2d943b5942 | ||
|
|
dd4e0ee6f9 | ||
|
|
bb8d4abb9e | ||
|
|
64069a6e7f | ||
|
|
9ead803b6c | ||
|
|
af6387f1fc | ||
|
|
6d0764bbb8 | ||
|
|
eb9d017882 | ||
|
|
ec630ad1b1 | ||
|
|
df30c7c764 | ||
|
|
7a73f9c8c7 | ||
| 045612c00f | |||
| 25d3587545 | |||
| 4ffd78545d | |||
|
|
14d018c327 | ||
|
|
f1c88797a3 | ||
|
|
f882e18be9 | ||
|
|
0fc9bb57ae | ||
|
|
22dcd9f7ae | ||
|
|
7d361b2203 | ||
|
|
8131d37d8e | ||
|
|
6485c3efee | ||
|
|
2bfbff9b14 | ||
|
|
9a72141567 | ||
|
|
6992b5186e | ||
|
|
18db571507 | ||
|
|
09794ccb68 | ||
|
|
7644f52dfd | ||
|
|
21fa636e0c | ||
|
|
df755d30ee | ||
|
|
7f0c4626b0 | ||
|
|
ec42fda336 | ||
|
|
def9ff9746 | ||
|
|
a7df0bde3e | ||
|
|
1cec5a6067 | ||
|
|
a58cbffb81 | ||
|
|
30630c3358 | ||
|
|
98f9693cff | ||
|
|
228be95f9c | ||
|
|
457c58a660 | ||
|
|
39cec6f11d | ||
|
|
8f5f72d9fd | ||
|
|
b21eb3f118 | ||
|
|
136ec5b49b | ||
|
|
e84cc8e8b1 | ||
|
|
26fb76f95f | ||
|
|
68a8f9d356 | ||
|
|
f541f47476 | ||
|
|
9d4cfbd270 | ||
| 1869d05591 | |||
| 3f8b1008b4 | |||
| e671949dd2 | |||
|
|
dedeb13f46 | ||
|
|
9d0fbb9ea9 | ||
|
|
97dcf98e2b | ||
|
|
25b25acc94 | ||
|
|
ac6df47818 | ||
|
|
0f828cbd3a | ||
|
|
eb8d39fbe3 | ||
|
|
11831a2b24 | ||
|
|
e9816a22a3 | ||
|
|
8165b5417f | ||
|
|
714771fbc3 | ||
|
|
a2a8dc4489 | ||
|
|
bbe39f8523 | ||
|
|
a69490a23a | ||
|
|
b6223c3805 | ||
| e3524c112a | |||
| 8c0141508d | |||
| 8206450456 | |||
| a5dfe0ec51 | |||
| 9a4aaf47bc | |||
|
|
485e4245ff | ||
|
|
72a7cd9685 | ||
|
|
95a7c4f474 | ||
|
|
f208601bc4 | ||
|
|
d5b9fda636 | ||
| 99277491c8 | |||
| 2828dfcc75 | |||
| b14aa668c2 | |||
| 47de6243d3 | |||
| 7bad2c886b | |||
| c4e496a5ff | |||
| 7ee3e00e49 | |||
| 2f39b63723 | |||
| d279388884 | |||
|
|
358e1ccf42 | ||
| 236d56bbf0 | |||
| 68f9b8339f | |||
| 4a4356b72a | |||
| b71675585c | |||
| bebb356425 | |||
| 9fd36d8d53 | |||
| 1137088e20 | |||
| 6f2b2ab883 | |||
| d8fcc9160d | |||
| cb6843e08b | |||
| 162eac3bdf | |||
| e2d2b5b4b3 | |||
| de3668db96 | |||
| 93a2ec3186 | |||
| 354c930d85 | |||
| e97cdce467 | |||
| 13826f4934 | |||
| 1cdbc53dc5 | |||
| 51b9517897 | |||
| a74a4b390b | |||
| 79b134164a | |||
| ced78e0b1f | |||
| 9087025418 | |||
| 9a5263e508 | |||
| 75571e2eb0 | |||
| b5ed4b9a9c | |||
| 2daf08d22d | |||
| 2e51c73ac0 | |||
| 298bb01762 | |||
| a02c7bdc44 | |||
| feb3c98459 | |||
| 823d8bed7e | |||
| 68813580eb | |||
| 6bc1b91cc6 | |||
| 56f91526e6 | |||
| dbc6a16a98 | |||
| 4d3c5ad732 | |||
| 307ff5c1ca | |||
| 3b86a17b49 | |||
| 54fe849eef | |||
| c023711d16 | |||
| 9bc0f44777 | |||
| 4a3a767cb2 | |||
| d0c37fef67 | |||
| 11863cd7b0 | |||
| 1c696e2561 | |||
| 43c5e450a8 | |||
| 0a84d8a1d8 | |||
| 354a4d08be | |||
| af93c0e488 | |||
| b8aa322691 | |||
| a87987da87 | |||
| 8b8e9d3980 | |||
| a19f39308e | |||
| 0b23fbed86 | |||
| d1eeb559cb | |||
| 2e4a82418f | |||
| fcdb400edf | |||
|
|
f80fb35e35 | ||
| 056a7b722b | |||
| f4c0f353f3 | |||
| 3fb7fbac51 | |||
| bd70746278 | |||
| c744d945a4 | |||
| f0696b3f60 | |||
| bc36f24551 | |||
| 82b68b5f4e | |||
| e8238b5ed7 | |||
| 599f21fabb | |||
| e91e2c1c96 | |||
| 9f421bfd6d | |||
| 3a4071505e | |||
| 344c4a324c | |||
| 0aca118546 | |||
| 124c490f25 | |||
| 0a5622c78e | |||
| 373f21e247 | |||
| e80292e75d | |||
| 7173381d9b | |||
| 78f31a65d4 | |||
| 2e19fee83c | |||
| f6754ff180 | |||
| 3f74c77755 | |||
| c904e22c0f | |||
| 27023e50ae | |||
| 0f2f010f94 | |||
| cf2989d587 | |||
| 94c78ebb72 | |||
| d9be39b839 | |||
| 7fcad7b5c5 | |||
| cfeef3651f | |||
| e849e8a5c2 | |||
| 5bd21e68df | |||
| 71597ca89b | |||
| a3cf2877b8 | |||
| 23e9ccb236 | |||
| 7d97554cb1 | |||
| 5f8c0d67b8 | |||
| b468325f64 | |||
| 39d23424d6 | |||
| eba2dd0171 | |||
| 1bdd44085c | |||
| 596af6afe6 | |||
| 3f3ddc3955 | |||
| a58c60b8ce | |||
| b42ad0dd83 | |||
| e0fbe841c1 | |||
| 3523b2c1a3 | |||
| c15c14ffcd | |||
| 8cd729480f | |||
| 638bc2816f | |||
| 826ea17cdd | |||
| 19e221bb32 | |||
| 85de69bca7 | |||
| 7d5b9d0f63 | |||
| 816ab71d83 | |||
| c230b5c40d | |||
| 71620a320d | |||
| c016b0aecc | |||
| 95b3028c95 | |||
| ae4ac801a0 | |||
| bda3e42e2b | |||
| 4dc1df5887 | |||
| 302b9f5df4 | |||
| c2d89c7a60 | |||
| 45574753c7 | |||
| c84c618ef0 | |||
| 98d17d60a5 | |||
| 4c1c6e76fc | |||
| ac1441aba5 | |||
| 5d2ff40dc9 | |||
| 63f2b80515 | |||
| 52b25fbc72 | |||
| b94a887534 | |||
| f6699fbfda | |||
| 9743b05a78 | |||
| 9681b7b5ef | |||
| 017bc50698 | |||
| 1e94456a74 | |||
| 2fcba80f3a | |||
| fc4cbe84f0 | |||
| 011abf62a0 | |||
|
|
9c96857262 | ||
| 4f55ec6ea8 | |||
| 8139088b39 | |||
| 44f7d13449 | |||
| 16b0682229 | |||
| a77798f293 | |||
| 08050ff616 | |||
| 12b080152b | |||
| df3d660e83 | |||
| 4908709296 | |||
| 5717ae1bf1 | |||
| 872c8d9d81 | |||
| 0b6110f0f9 | |||
| 6df5e9ebe9 | |||
| 2b9fd74a1d | |||
| 4a4b3c6aeb | |||
| 7979f74bea | |||
| b0336fb495 | |||
| 328fcd23f4 | |||
| 9c0951ae58 | |||
| 3f51561271 | |||
| 1787c0e74e | |||
| 49faacda1c | |||
| 339eeff1ff | |||
| 849212fd2f | |||
| 356b2b06e4 | |||
| b6eefbdb36 | |||
| 2a72601153 | |||
| 2bf7358207 | |||
| 8c6e2ef461 | |||
| ce0cbb6ee2 | |||
| 67ef3bb90c | |||
| b82af419f8 | |||
| 49ff9a7edf | |||
| 53ebdf4f14 | |||
| 4762b54701 | |||
| aa288ac406 | |||
| 2228dbf0f4 | |||
| 9ca1c8e459 | |||
| 61414d62f4 | |||
| f97fed3b9b | |||
| d45281d137 | |||
| 1bb6ad41b2 | |||
| 56c180183e | |||
| af8d983cca | |||
| 0a49232ebd | |||
| cff8e26428 | |||
| e892bccb32 | |||
| 68ccf37fd5 | |||
| 659c528744 | |||
| b1560dd694 | |||
| 0de86ac66c | |||
| 06e5d517cc | |||
| 35ca041bc2 | |||
| 576a334dc9 | |||
| 294aee5d12 | |||
| 0859cec853 | |||
| 23f2978a64 | |||
| a2400172e2 | |||
| 5376f4bff8 | |||
| 0497890cb0 | |||
| 2848c4e77b | |||
| 8fa3ba1b18 | |||
| b4f36dd258 | |||
| 008902d3b7 | |||
| 4764c07f3b | |||
| 8f0cfa8614 | |||
| 865e1969e6 | |||
|
|
bfddc42f5e | ||
| dc0b8deccf | |||
| b674d14b49 | |||
| d594d3b085 | |||
| bef85bf93a | |||
| 76eaefc95b | |||
|
|
2d488a67f2 | ||
|
|
d997b1378d | ||
| 720f98f9bd | |||
| ddea9e78a9 | |||
| c429cb41c0 | |||
| ae286cec14 | |||
| 31d631b155 | |||
| 20142d5f94 | |||
| ef186d55c6 | |||
| 8b847ae9fa | |||
|
|
a4ef657897 | ||
| f44556e281 | |||
| 8a895b2d20 | |||
| 61f32449dd | |||
|
|
07f8583c3d | ||
|
|
69f11c9d4e | ||
|
|
1ffc079042 | ||
| 5fa3f412c0 | |||
| b72cad5316 | |||
|
|
d59ab89426 | ||
| ea019321e6 | |||
| 6967def950 | |||
| 6d4cac427f | |||
| 152b2d5427 | |||
| 9d28fbe7b5 | |||
| c846dfc75a | |||
| ee7eb4ef51 | |||
|
|
57bfe3d801 | ||
| a5ee96f988 | |||
| 7b0eddeac5 | |||
| e8e52db9b1 | |||
| cddbf558e6 | |||
| 84523869e8 | |||
| 8067fd5313 | |||
|
|
d2dc756a34 | ||
| 2af1dbf3a6 | |||
| ebab6f08ee | |||
| 4a2b21855a | |||
| 42d5edec26 | |||
| f368e43158 | |||
| 09eb8c9f4d | |||
| 209e709163 | |||
| d20a2be7e6 | |||
| bd68f8fc5a | |||
|
|
1a05f7d85d | ||
|
|
d9ff429c28 | ||
| 3554895a5d | |||
| 16491c142a | |||
| 859fea5ff5 | |||
| 34c73e89db | |||
| 48e43869c7 | |||
| 963fb58309 | |||
| 3ef3be4d16 | |||
| bae0e3bcc1 | |||
| 3e99d821a5 | |||
|
|
acb5051eec | ||
|
|
b76882dd1d | ||
|
|
978946baab | ||
|
|
d202f14c14 | ||
|
|
17a85e517a | ||
|
|
c5bc5deff0 | ||
|
|
b7f04957a5 | ||
|
|
b0f5f96eee | ||
|
|
fd76a3c6fd | ||
| b31482881b | |||
|
|
87231d7fa4 | ||
|
|
4d18a1335c | ||
|
|
424a417a13 | ||
|
|
96d23bdf22 | ||
| a8e77b8df8 | |||
| 5413569ce3 | |||
| d80b85ac8c | |||
| 40bc35935f | |||
| a6060f468d | |||
| 6ec9d51a1e | |||
| de28a5e74e | |||
| 3ba503604b | |||
| 6d48b53861 | |||
| 0b7697d172 | |||
| b9850fa085 | |||
| ecb3978bdd | |||
| fc57a9db6c | |||
| 6c9c2a6c1a | |||
| 3e0529d515 | |||
| 7d8d89fbbd | |||
| c43f3c2fd7 | |||
| 403ed8b250 | |||
| 9ccb2b2737 | |||
| 424a282847 | |||
| 309b6cbcaf | |||
| 72ad14119a | |||
|
|
652ed50d09 | ||
| 6070a7af2e | |||
| 8fd8c2802b | |||
| 923b923745 | |||
| 59c8031372 | |||
| 0058089e7d | |||
| d5a840388c | |||
| 4b07d7d5b1 | |||
| 2fffc25128 | |||
| 4a4501276c | |||
|
|
c8e3735dd6 | ||
|
|
61267e40e7 | ||
|
|
c0b664e1e4 | ||
|
|
e57c319658 | ||
|
|
e54ba826b3 | ||
|
|
9b8784b4c4 | ||
|
|
51a7b7a7d4 | ||
|
|
d761b474cf | ||
|
|
51be585b9d | ||
|
|
76be5037fd | ||
| aee0da2c64 | |||
| f1610e6603 | |||
| a7a1766809 | |||
| 5f83314d56 | |||
| c784f40c55 | |||
| 6a172d135b | |||
| 13f4981066 | |||
|
|
849b91dde2 | ||
|
|
66b4c48d92 | ||
|
|
2e64da4cac | ||
| 323ddcc11a | |||
| 175000efd1 | |||
| 6f94fc48c1 | |||
|
|
18d1d0d9f7 | ||
|
|
47edea47ae | ||
|
|
1714cf8050 | ||
|
|
7366e9a47f | ||
|
|
e58589cfbd | ||
|
|
2999e0e5eb | ||
|
|
fa7bc27124 | ||
|
|
f5be9d3c67 | ||
|
|
2c46e8909a | ||
|
|
8b042f30dc | ||
|
|
46761926d2 | ||
|
|
88d6a8e513 | ||
|
|
557ae19297 | ||
|
|
9c10a56dda | ||
|
|
895b068321 | ||
|
|
fb98c5fe9a | ||
|
|
0ec604f21e | ||
|
|
bcd9dd1bb5 | ||
|
|
61bcd253f8 | ||
|
|
fb40dbdabc | ||
|
|
0990192cd6 | ||
|
|
1cf2d69534 | ||
|
|
0489dc39e0 | ||
|
|
88b9645be1 |
36
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report something broken in the app
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
<!--
|
||||
Please use the template below for your bug report. Issues that do not follow this
|
||||
template, or that do not contain all necessary information (namely, description of
|
||||
the problem, steps to reproduce, phone, phone operating system, and app version)
|
||||
will be closed without further consideration.
|
||||
-->
|
||||
|
||||
## Pre-submission checklist
|
||||
- [ ] I am submitting a bug report, not a feature request.
|
||||
- [ ] I have searched for similar issues, but did not find any matches.
|
||||
|
||||
## Description
|
||||
A clear and concise description of what the problem is.
|
||||
|
||||
## Steps to reproduce
|
||||
1. Go to ...
|
||||
2. Click on ....
|
||||
3. Scroll down to ....
|
||||
4. See error
|
||||
|
||||
## System information
|
||||
- Phone: [e.g. Google Pixel 4]
|
||||
- Phone Operating System: [e.g. Android 10]
|
||||
- App version: [e.g. 2.0.2]
|
||||
|
||||
## Screenshots
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Feature requests
|
||||
url: https://github.com/iSoron/uhabits/discussions/categories/feature-requests?discussions_q=category%3A%22Feature+Requests%22+sort%3Atop
|
||||
about: Submit ideas for new features and for improving existing features
|
||||
- name: Help & FAQ
|
||||
url: https://github.com/iSoron/uhabits/discussions/categories/help-faq
|
||||
about: Ask questions about using the app or setting up the project
|
||||
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gradle"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 10
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
48
.github/workflows/main.yml
vendored
@@ -1,31 +1,31 @@
|
||||
name: Build & Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macOS-latest
|
||||
Test:
|
||||
runs-on: self-hosted
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install Java Development Kit 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Build APK & Run small tests
|
||||
run: android/build.sh build
|
||||
- name: Run medium tests
|
||||
uses: ReactiveCircus/android-emulator-runner@v2.2.0
|
||||
with:
|
||||
api-level: 29
|
||||
script: android/build.sh medium-tests
|
||||
- name: Check out source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build project
|
||||
run: ./build.sh build
|
||||
|
||||
- name: Run Android tests
|
||||
run: ./build.sh android-tests-parallel 28 29 30 32 33 34
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Build
|
||||
path: android/uhabits-android/build/outputs/
|
||||
name: build
|
||||
path: |
|
||||
build/*log
|
||||
uhabits-android/build/outputs
|
||||
|
||||
|
||||
42
.github/workflows/publish.yml
vendored
@@ -1,42 +0,0 @@
|
||||
name: Build, Test & Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macOS-latest
|
||||
env:
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install GPG
|
||||
uses: olafurpg/setup-gpg@v2
|
||||
- name: Decrypt secrets
|
||||
env:
|
||||
GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }}
|
||||
run: .secret/decrypt.sh
|
||||
- name: Install Java Development Kit 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Build APK & Run small tests
|
||||
env:
|
||||
RELEASE: 1
|
||||
run: android/build.sh build
|
||||
- name: Run medium tests
|
||||
uses: ReactiveCircus/android-emulator-runner@v2.2.0
|
||||
env:
|
||||
RELEASE: 1
|
||||
with:
|
||||
api-level: 29
|
||||
script: android/build.sh medium-tests
|
||||
- name: Upload build to GitHub
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: Build
|
||||
path: android/uhabits-android/build/outputs/
|
||||
- name: Upload APK to Google Play
|
||||
run: cd android && ./gradlew publishReleaseApk
|
||||
6
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
*.perspectivev3
|
||||
*.swp
|
||||
*~.nib
|
||||
*.hprof
|
||||
.DS_Store
|
||||
._.DS_Store
|
||||
.externalNativeBuild
|
||||
@@ -11,8 +12,9 @@
|
||||
.idea
|
||||
.secret
|
||||
build
|
||||
build/
|
||||
captures
|
||||
local.properties
|
||||
node_modules
|
||||
*xcuserdata*
|
||||
*.sketch
|
||||
crowdin.yml
|
||||
kotlin-js-store
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/sh
|
||||
cd "$(dirname "$0")"
|
||||
if [ -z "$GPG_PASSWORD" ]; then
|
||||
echo Env variable GPG_PASSWORD must be defined
|
||||
exit 1
|
||||
fi
|
||||
gpg \
|
||||
--quiet \
|
||||
--batch \
|
||||
--yes \
|
||||
--decrypt \
|
||||
--passphrase="$GPG_PASSWORD" \
|
||||
--output secret.tar.gz \
|
||||
secret
|
||||
tar -xzf secret.tar.gz
|
||||
rm secret.tar.gz
|
||||
BIN
.secret/secret
459
CHANGELOG.md
@@ -1,213 +1,372 @@
|
||||
# Changelog
|
||||
|
||||
### 1.8.9 (Nov 18, 2020)
|
||||
## [2.2.0] -- 2024-01-30
|
||||
### Added
|
||||
- Add support for Android 14 (@iSoron, @hiqua)
|
||||
- Allow user to change app language (@leondzn)
|
||||
|
||||
* Remove INTERNET permission
|
||||
* Manage exceptions for when activities don't exist to handle intents (#181)
|
||||
* MemoryHabitList: Inherit parent's order (#598)
|
||||
* Remove notification groups; revert to default system behavior
|
||||
* Remove SyncManager and Internet permission
|
||||
### Fixed
|
||||
- Implement workaround to make notifications non-dismissible in Android 14 (@iSoron, #1872)
|
||||
- Fix splash screen background color in dark mode (@SIKV, #1888)
|
||||
|
||||
### 1.8.8 (June 21, 2020)
|
||||
## [2.1.3] -- 2023-08-28
|
||||
### Fixed
|
||||
- Use text input on Samsung devices (@iSoron, #1719)
|
||||
- Prevent crash if alarm permission is revoked (@iSoron)
|
||||
- Adjust widget colors (@iSoron)
|
||||
- Fix bug preventing screens from updating at midnight (@iSoron)
|
||||
- Fix skip button in locales that use comma instead of dot (@iSoron, #1721)
|
||||
|
||||
* Make small changes to the habit scheduling algorithm, so that "1 time every x days" habits work more predictably.
|
||||
* Fix crash when saving habit
|
||||
## [2.1.2] -- 2023-05-26
|
||||
### Fixed
|
||||
- Fix bug that caused widget to enter checkmark on wrong date (@iSoron, #1541)
|
||||
- Fix widget corners on Android 12 (@iSoron)
|
||||
- Fix bug that caused notes to be lost when editing a checkmark (@iSoron, #1566)
|
||||
- Prevent soft keyboard from covering entry popup (@iSoron)
|
||||
- Accept comma (instead of dot) in certain locales (@iSoron)
|
||||
|
||||
### 1.8.0 (Jan 1, 2020)
|
||||
### Changed
|
||||
- Remove update delay after entering a checkmark (@iSoron)
|
||||
|
||||
* New bar chart showing number of repetitions performed in each week, month, quarter or year.
|
||||
* Improved calculation of streaks for non-daily habits: performing habits on irregular weekdays will no longer break your streak.
|
||||
* Many more colors to choose from (now 20 in total).
|
||||
* Ability to customize how transparent the widgets are on your home screen.
|
||||
* Ability to customize the first day of the week.
|
||||
* Yes/No buttons on notifications, instead of just "Check".
|
||||
* Automatic dark theme according to phone settings (Android 10).
|
||||
* Smaller APK and backup files.
|
||||
* Many other internal code changes improving performance and stability.
|
||||
### Removed
|
||||
- Remove stack widgets (@iSoron)
|
||||
|
||||
### 1.7.11 (Aug 10, 2019)
|
||||
|
||||
* Fix bug that produced corrupted CSV files in some countries
|
||||
## [2.1.1] -- 2022-09-24
|
||||
### Fixed
|
||||
- Fix Tasker plugin (@iSoron, #1503)
|
||||
|
||||
### 1.7.10 (June 15, 2019)
|
||||
## [2.1.0] -- 2022-09-10
|
||||
### Added
|
||||
- Allow user to add notes to specific dates (@vbh, #1103)
|
||||
- Allow user to track "at most" numerical habits (@KristianTashkov, #1101)
|
||||
- Allow user to add skips to measurable habits (@kalina559, #1319)
|
||||
- Bring back custom frequencies (x times in y days) (@hiqua, #1079)
|
||||
- Improve number picker (@hiqua, @iSoron, #1082, #1370)
|
||||
- Add new checkmark and number picker (@iSoron, #1370)
|
||||
- Allow user to import numerical habits from HabitBull (@hiqua, #1278)
|
||||
- Add support for Android 13 themed icons (@cheeeeer, #1497)
|
||||
|
||||
* Fix bug that prevented some devices from showing notifications.
|
||||
* Update targetSdk to Android Pie (API level 28)
|
||||
### Removed
|
||||
- Hide snooze button Android 12 notifications (@hiqua, #1226)
|
||||
- Remove preference to set LED lights (@iSoron)
|
||||
|
||||
### 1.7.8 (April 21, 2018)
|
||||
### Changed
|
||||
- Hide failed habits along with completed ones (@hiqua, #1052)
|
||||
- Cycle through all checkmark states when toggling (@iSoron)
|
||||
- Add delay after toggling a habit (@hiqua, @kalina559, #1147)
|
||||
- Small theme improvements (@KristianTashkov, #1113)
|
||||
- Left-align habit notes (@iSoron)
|
||||
- Increase target SDK to 31 (@hiqua)
|
||||
|
||||
* Add support for adaptive icons (Oreo)
|
||||
* Add support for notification channels (Oreo)
|
||||
* Update translations
|
||||
### Fixed
|
||||
- Fix small dialog buttons (@kalina559, #1096)
|
||||
- Fix invalid CSV files (@hiqua, #1177)
|
||||
- Fix small issues in calendar chart (@kalina559, #1314)
|
||||
- Resort habit list after edit (@hiqua, #1350)
|
||||
- Fix marker scaling in frequency display (@eduebernal, #1425)
|
||||
- Fix widgets not working correctly on API 33 (@iSoron, #1488)
|
||||
|
||||
### 1.7.7 (September 30, 2017)
|
||||
### Refactoring & Testing
|
||||
- Replace raster icons by vector assets (@kalina559)
|
||||
- Remove JVM dependencies from uhabits-core module (@sgallese)
|
||||
- Add various missing tests (@sgallese)
|
||||
- Upgrade project dependencies (@hiqua, @sgallese)
|
||||
|
||||
* Fix bug that caused reminders to show repeatedly on DST changes
|
||||
## [2.0.3] - 2021-08-21
|
||||
### Fixed
|
||||
- Improve automatic checkmarks for monthly habits (@iSoron, #947)
|
||||
- Fix small theme issues (@iSoron)
|
||||
- Fix ANR on some Samsung phones (@iSoron, #962)
|
||||
- Fix dates before the year 2000 (@iSoron, #967)
|
||||
- Fix notification adding checkmarks to the wrong day (@hiqua, #969)
|
||||
- Fix crashes in widgets (@hiqua, @iSoron, #907, #966, #965)
|
||||
- Fix crash when moving habits (@hiqua, #968)
|
||||
|
||||
### 1.7.6 (July 18, 2017)
|
||||
## [2.0.2] - 2021-05-23
|
||||
|
||||
* Fix bug that caused widgets not to render sometimes
|
||||
* Fix other minor bugs
|
||||
* Update translations
|
||||
### Changed
|
||||
- Make checkmark widget resizable
|
||||
|
||||
### 1.7.3 (May 30, 2017)
|
||||
### Fixed
|
||||
- Fix crash caused by numerical habits with zero target (@iSoron, #903)
|
||||
- Fix small issues with font size (@iSoron)
|
||||
- Allow fractional target values (@sumanabhi, #911)
|
||||
- Fix IllegalStateException in androidx.customview.view (@iSoron, #906)
|
||||
- Fix crash when selecting habit frequency in some languages (@iSoron, #926)
|
||||
- Fix IllegalArgumentException in RingView (@iSoron, #904)
|
||||
|
||||
* Improve performance of 'sort by score'
|
||||
* Other minor bug fixes
|
||||
## [2.0.1] - 2021-05-09
|
||||
|
||||
### 1.7.2 (May 27, 2017)
|
||||
### Added
|
||||
- Make midnight delay optional and disabled by default (@hiqua)
|
||||
- Add arrows to sort menu (@iSoron)
|
||||
|
||||
* Fix crash at startup
|
||||
### Removed
|
||||
- Temporarily remove experimental device sync functionality. This feature will be re-added in
|
||||
Loop 2.1.
|
||||
|
||||
### 1.7.1 (May 21, 2017)
|
||||
### Changed
|
||||
- Make implicit checkmarks easier to read (@iSoron)
|
||||
- Update and improve list of translators (@hiqua, @iSoron)
|
||||
|
||||
* Fix crash (BadParcelableException)
|
||||
* Fix layout for RTL languages such as Arabic
|
||||
* Automatically detect and reject invalid database files
|
||||
* Add Hebrew translation
|
||||
### Fixed
|
||||
- Disable transparency for stacked widgets (@hiqua)
|
||||
- Fix various color issues on the dark theme (@hiqua, @iSoron)
|
||||
- Fix "customize notifications" on older devices (@hiqua)
|
||||
- Fix snooze button in notifications when device is locked (@hiqua)
|
||||
- Fix a crash when deleting habits (@engineering4good)
|
||||
- Fix checkmark widget not rendering properly on some Samsung phones (@iSoron)
|
||||
|
||||
### 1.7.0 (Mar 31, 2017)
|
||||
### Refactoring & Testing
|
||||
- Finish conversion of the entire project to Kotlin (@hiqua, @iSoron, @MarKco)
|
||||
- Automatically run large tests on GitHub Actions (@iSoron)
|
||||
- Remove unused v21 resources (@hiqua)
|
||||
|
||||
* Sort habits automatically
|
||||
* Allow swiping the header to see previous days
|
||||
* Import backups directly from Google Drive or Dropbox
|
||||
* Refresh data automatically at midnight
|
||||
* Other minor bug fixes and enhancements
|
||||
## [2.0.0-alpha] - 2020-11-29
|
||||
|
||||
### 1.6.2 (Oct 13, 2016)
|
||||
### Added
|
||||
- Track numeric habits (@iSoron, @namnl)
|
||||
- Skip days without breaking streak (@KristianTashkov)
|
||||
- Sort habits by status (@hiqua)
|
||||
- Sort habits in reverse order (@iSoron)
|
||||
- Add notes to habits (@recheej)
|
||||
- Improve readibility of charts (@chennemann)
|
||||
- Delay new day until 3am (@KristianTashkov)
|
||||
- Export backups daily (@iSoron)
|
||||
|
||||
* Fix crash on Android 4.1
|
||||
### Removed
|
||||
- Drop support to devices older than Android 6.0 (API 23)
|
||||
|
||||
### 1.6.1 (Oct 10, 2016)
|
||||
### Fixed
|
||||
- Reset chart offset when switching scale (@alxmjo)
|
||||
- Don't show reminders from archived habits (@KristianTashkov)
|
||||
- Lapses on non-daily habits decrease the score too much (@iSoron)
|
||||
- Update widgets at midnight (@KristianTashkov)
|
||||
|
||||
* Fix a crash at startup when database is corrupted
|
||||
### Refactoring
|
||||
- Convert files to Kotlin (@olegivo)
|
||||
|
||||
### 1.6.0 (Oct 10, 2016)
|
||||
## [1.8.12] - 2021-01-30
|
||||
|
||||
* Add option to make notifications sticky
|
||||
* Add option to hide completed habits
|
||||
* Display total number of repetitions for each habit
|
||||
* Pebble integration: check/snooze habits from the watch
|
||||
* Tasker/Locale integration: allow third-party apps to add checkmarks
|
||||
* Export an unified CSV file, with checkmarks for all the habits
|
||||
* Increase width of name column according to screen size
|
||||
* Stop showing reminders for archived habits
|
||||
* Add Danish, Dutch, Greek, Hindi and Portuguese (PT) translations
|
||||
* Other minor fixes and enhancements
|
||||
- Fix bug that caused incorrect check marks to show after scrolling (#713)
|
||||
- Fix issue preventing widgets from updating at midnight (#680)
|
||||
|
||||
### 1.5.6 (Jun 19, 2016)
|
||||
## [1.8.11] - 2020-12-29
|
||||
|
||||
* Fix bug that prevented checkmark widget from working
|
||||
- Fix theme issues on Xiaomi phones
|
||||
|
||||
### 1.5.5 (Jun 19, 2016)
|
||||
## [1.8.10] - 2020-11-26
|
||||
|
||||
* Fix bug that prevented check button on notification to work sometimes
|
||||
* Fix bug that caused back button to apparently erase some checkmarks
|
||||
* Complete French translation
|
||||
* Add Croatian and Slovenian translations
|
||||
- Update translations
|
||||
|
||||
### 1.5.4 (May 29, 2016)
|
||||
## [1.8.9] - 2020-11-18
|
||||
|
||||
* Fix crash upon opening settings screen in some phones
|
||||
* Fix missing folders in CSV archive
|
||||
* Add Serbian translation
|
||||
- Manage exceptions when activities don't exist to handle intents (#181)
|
||||
- MemoryHabitList: Inherit parent's order (#598)
|
||||
- Remove notification groups; revert to default system behavior
|
||||
- Remove SyncManager and Internet permission
|
||||
|
||||
### 1.5.3 (May 22, 2016)
|
||||
## [1.8.8] - 2020-06-21
|
||||
|
||||
* Complete Arabic and Czech translations
|
||||
* Fix crash at startup
|
||||
* Fix checkmark widget on custom launchers
|
||||
- Make small changes to the habit scheduling algorithm, so that "1 time every x days" habits work more predictably.
|
||||
- Fix crash when saving habit
|
||||
|
||||
### 1.5.2 (May 19, 2016)
|
||||
## [1.8.0] - 2020-01-01
|
||||
|
||||
* Fix missing attachment on bug reports
|
||||
* Fix bug that prevents some widgets from rendering
|
||||
* Complete Japanese translation
|
||||
- New bar chart showing number of repetitions performed in each week, month, quarter or year.
|
||||
- Improved calculation of streaks for non-daily habits: performing habits on irregular weekdays will no longer break your streak.
|
||||
- Many more colors to choose from (now 20 in total).
|
||||
- Ability to customize how transparent the widgets are on your home screen.
|
||||
- Ability to customize the first day of the week.
|
||||
- Yes/No buttons on notifications, instead of just "Check".
|
||||
- Automatic dark theme according to phone settings (Android 10).
|
||||
- Smaller APK and backup files.
|
||||
- Many other internal code changes improving performance and stability.
|
||||
|
||||
### 1.5.1 (May 17, 2016)
|
||||
## [1.7.11] - 2019-08-10
|
||||
|
||||
* Fix build on F-Droid
|
||||
- Fix bug that produced corrupted CSV files in some countries
|
||||
|
||||
### 1.5.0 (May 15, 2016)
|
||||
## [1.7.10] - 2019-06-15
|
||||
|
||||
* Add night mode, with AMOLED support
|
||||
* Backport material design to older devices
|
||||
* Display more information on statistics screen
|
||||
* Display score on main screen and checkmark widget
|
||||
* Make widgets react immediately to touch
|
||||
* Reschedule reminders after reboot
|
||||
* Pick first day of the week according to country
|
||||
* Add option to reverse order of days on main screen
|
||||
* Add option to change notification sounds
|
||||
* Add Catalan, Indonesian, Turkish, Ukrainian translations
|
||||
* Switch between Simplified/Traditional Chinese according to country
|
||||
- Fix bug that prevented some devices from showing notifications.
|
||||
- Update targetSdk to Android Pie (API level 28)
|
||||
|
||||
### 1.4.1 (April 9, 2016)
|
||||
## [1.7.8] - 2018-04-21
|
||||
|
||||
* Show error message on widgets, instead of crashing
|
||||
* Complete French translation
|
||||
* Minor fixes to other translations
|
||||
- Add support for adaptive icons (Oreo)
|
||||
- Add support for notification channels (Oreo)
|
||||
- Update translations
|
||||
|
||||
### 1.4.0 (April 7, 2016)
|
||||
## [1.7.7] - 2017-09-30
|
||||
|
||||
* Ability to import data from third-party apps
|
||||
* Ability to save and restore full database backup
|
||||
* Show more information on streak chart
|
||||
* Simplify interface for creating habits
|
||||
* Add link to Frequently Asked Questions (FAQ)
|
||||
* Reduce app loading time and lag on widgets
|
||||
* Generate bug reports on crash and from settings screen
|
||||
* Disable vibration according to phone settings
|
||||
* Add Czech translation
|
||||
* Fix wrong month names for some languages
|
||||
- Fix bug that caused reminders to show repeatedly on DST changes
|
||||
|
||||
### 1.3.3 (March 20, 2016)
|
||||
## [1.7.6] - 2017-07-18
|
||||
|
||||
* Add Spanish and Korean translations
|
||||
* Make small corrections to other translations
|
||||
* Fix incorrect date in history calendar
|
||||
- Fix bug that caused widgets not to render sometimes
|
||||
- Fix other minor bugs
|
||||
- Update translations
|
||||
|
||||
### 1.3.2 (March 18, 2016)
|
||||
## [1.7.3] - 2017-05-30
|
||||
|
||||
* Add Arabic, Italian, Polish, Russian and Swedish translations
|
||||
* Minor fixes to German and French translations
|
||||
* Minor bug fixes
|
||||
- Improve performance of 'sort by score'
|
||||
- Other minor bug fixes
|
||||
|
||||
### 1.3.1 (March 15, 2016)
|
||||
## [1.7.2] - 2017-05-27
|
||||
|
||||
* Fixes crash on devices with large screen, such as the Nexus 10
|
||||
* Fixes crash when clicking widgets and reminders of deleted habits
|
||||
* Other minor bug fixes
|
||||
- Fix crash at startup
|
||||
|
||||
### 1.3.0 (March 12, 2016)
|
||||
## [1.7.1] - 2017-05-21
|
||||
|
||||
* New frequency plot: view total repetitions per day of week
|
||||
* New history editor: put checkmarks in the past
|
||||
* Add German, French and Japanese translations
|
||||
* Add about screen, with credits to all contributors
|
||||
* Fix small bug that prevented habit from being reordered
|
||||
* Fix small bug caused by rotating the device
|
||||
- Fix crash (BadParcelableException)
|
||||
- Fix layout for RTL languages such as Arabic
|
||||
- Automatically detect and reject invalid database files
|
||||
- Add Hebrew translation
|
||||
|
||||
### 1.2.0 (March 4, 2016)
|
||||
## [1.7.0] - 2017-03-31
|
||||
|
||||
* Ability to export habit data as CSV
|
||||
* Widgets (checkmark, history, score and streaks)
|
||||
* More natural scrolling on data views (fling)
|
||||
* Minor UI improvements on pre-Lollipop devices
|
||||
* Fix crash on Samsung Galaxy TabS 8.4
|
||||
* Other minor bug fixes
|
||||
- Sort habits automatically
|
||||
- Allow swiping the header to see previous days
|
||||
- Import backups directly from Google Drive or Dropbox
|
||||
- Refresh data automatically at midnight
|
||||
- Other minor bug fixes and enhancements
|
||||
|
||||
### 1.1.1 (February 24, 2016)
|
||||
## [1.6.2] - 2016-10-13
|
||||
|
||||
* Show reminder only on chosen days of the week
|
||||
* Rearrange habits by long-pressing then dragging
|
||||
* Select and modify multiple habits simultaneously
|
||||
* 12/24 hour format according to phone preferences
|
||||
* Permanently delete habits
|
||||
* Usage hints during startup
|
||||
* Translation to Brazilian Portuguese and Chinese
|
||||
* Other minor fixes
|
||||
- Fix crash on Android 4.1
|
||||
|
||||
### 1.0.0 (February 19, 2016)
|
||||
## [1.6.1] - 2016-10-10
|
||||
|
||||
* Initial release
|
||||
- Fix a crash at startup when database is corrupted
|
||||
|
||||
## [1.6.0] - 2016-10-10
|
||||
|
||||
- Add option to make notifications sticky
|
||||
- Add option to hide completed habits
|
||||
- Display total number of repetitions for each habit
|
||||
- Pebble integration: check/snooze habits from the watch
|
||||
- Tasker/Locale integration: allow third-party apps to add checkmarks
|
||||
- Export an unified CSV file, with checkmarks for all the habits
|
||||
- Increase width of name column according to screen size
|
||||
- Stop showing reminders for archived habits
|
||||
- Add Danish, Dutch, Greek, Hindi and Portuguese (PT) translations
|
||||
- Other minor fixes and enhancements
|
||||
|
||||
## [1.5.6] - 2016-06-19
|
||||
|
||||
- Fix bug that prevented checkmark widget from working
|
||||
|
||||
## [1.5.5] - 2016-06-19
|
||||
|
||||
- Fix bug that prevented check button on notification to work sometimes
|
||||
- Fix bug that caused back button to apparently erase some checkmarks
|
||||
- Complete French translation
|
||||
- Add Croatian and Slovenian translations
|
||||
|
||||
## [1.5.4] - 2016-05-29
|
||||
|
||||
- Fix crash upon opening settings screen in some phones
|
||||
- Fix missing folders in CSV archive
|
||||
- Add Serbian translation
|
||||
|
||||
## [1.5.3] - 2016-05-22
|
||||
|
||||
- Complete Arabic and Czech translations
|
||||
- Fix crash at startup
|
||||
- Fix checkmark widget on custom launchers
|
||||
|
||||
## [1.5.2] - 2016-05-19
|
||||
|
||||
- Fix missing attachment on bug reports
|
||||
- Fix bug that prevents some widgets from rendering
|
||||
- Complete Japanese translation
|
||||
|
||||
## [1.5.1] - 2016-05-17
|
||||
|
||||
- Fix build on F-Droid
|
||||
|
||||
## [1.5.0] - 2016-05-15
|
||||
|
||||
- Add night mode, with AMOLED support
|
||||
- Backport material design to older devices
|
||||
- Display more information on statistics screen
|
||||
- Display score on main screen and checkmark widget
|
||||
- Make widgets react immediately to touch
|
||||
- Reschedule reminders after reboot
|
||||
- Pick first day of the week according to country
|
||||
- Add option to reverse order of days on main screen
|
||||
- Add option to change notification sounds
|
||||
- Add Catalan, Indonesian, Turkish, Ukrainian translations
|
||||
- Switch between Simplified/Traditional Chinese according to country
|
||||
|
||||
## [1.4.1] - 2016-04-09
|
||||
|
||||
- Show error message on widgets, instead of crashing
|
||||
- Complete French translation
|
||||
- Minor fixes to other translations
|
||||
|
||||
## [1.4.0] - 2016-04-07
|
||||
|
||||
- Ability to import data from third-party apps
|
||||
- Ability to save and restore full database backup
|
||||
- Show more information on streak chart
|
||||
- Simplify interface for creating habits
|
||||
- Add link to Frequently Asked Questions (FAQ)
|
||||
- Reduce app loading time and lag on widgets
|
||||
- Generate bug reports on crash and from settings screen
|
||||
- Disable vibration according to phone settings
|
||||
- Add Czech translation
|
||||
- Fix wrong month names for some languages
|
||||
|
||||
## [1.3.3] - 2016-03-20
|
||||
|
||||
- Add Spanish and Korean translations
|
||||
- Make small corrections to other translations
|
||||
- Fix incorrect date in history calendar
|
||||
|
||||
## [1.3.2] - 2016-03-18
|
||||
|
||||
- Add Arabic, Italian, Polish, Russian and Swedish translations
|
||||
- Minor fixes to German and French translations
|
||||
- Minor bug fixes
|
||||
|
||||
## [1.3.1] - 2016-03-15
|
||||
|
||||
- Fixes crash on devices with large screen, such as the Nexus 10
|
||||
- Fixes crash when clicking widgets and reminders of deleted habits
|
||||
- Other minor bug fixes
|
||||
|
||||
## [1.3.0] - 2016-03-12
|
||||
|
||||
- New frequency plot: view total repetitions per day of week
|
||||
- New history editor: put checkmarks in the past
|
||||
- Add German, French and Japanese translations
|
||||
- Add about screen, with credits to all contributors
|
||||
- Fix small bug that prevented habit from being reordered
|
||||
- Fix small bug caused by rotating the device
|
||||
|
||||
## [1.2.0] - 2016-03-04
|
||||
|
||||
- Ability to export habit data as CSV
|
||||
- Widgets (checkmark, history, score and streaks)
|
||||
- More natural scrolling on data views (fling)
|
||||
- Minor UI improvements on pre-Lollipop devices
|
||||
- Fix crash on Samsung Galaxy TabS 8.4
|
||||
- Other minor bug fixes
|
||||
|
||||
## [1.1.1] - 2016-02-24
|
||||
|
||||
- Show reminder only on chosen days of the week
|
||||
- Rearrange habits by long-pressing then dragging
|
||||
- Select and modify multiple habits simultaneously
|
||||
- 12/24 hour format according to phone preferences
|
||||
- Permanently delete habits
|
||||
- Usage hints during startup
|
||||
- Translation to Brazilian Portuguese and Chinese
|
||||
- Other minor fixes
|
||||
|
||||
## [1.0.0] - 2016-02-19
|
||||
|
||||
- Initial release
|
||||
|
||||
60
README.md
@@ -1,4 +1,15 @@
|
||||
# Loop Habit Tracker
|
||||
<h1 align="center">Loop Habit Tracker</h1>
|
||||
<p align="center">
|
||||
<a href="https://github.com/iSoron/uhabits/actions?query=workflow%3A%22Build+%26+Test%22">
|
||||
<img src="https://github.com/iSoron/uhabits/workflows/Build%20&%20Test/badge.svg" />
|
||||
</a>
|
||||
<a href="https://github.com/iSoron/uhabits/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/iSoron/uhabits" />
|
||||
</a>
|
||||
<a href="https://github.com/iSoron/uhabits/discussions">
|
||||
<img src="https://img.shields.io/badge/GitHub-Discussions-%23fc4ebc" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
Loop is a mobile app that helps you create and maintain good habits,
|
||||
allowing you to achieve your long-term goals. Detailed graphs and statistics
|
||||
@@ -6,8 +17,8 @@ show you how your habits improved over time. It is completely ad-free and open
|
||||
source.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://play.google.com/store/apps/details?id=org.isoron.uhabits&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-AC-global-none-all-co-pr-py-PartBadges-Oct1515-1"><img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/apps/en-play-badge-border.png" height="75px"/></a>
|
||||
<a href="http://f-droid.org/app/org.isoron.uhabits"><img alt="Get it on F-Droid" src="http://i.imgur.com/baSPE7X.png" height="75px"/></a>
|
||||
<a href="https://play.google.com/store/apps/details?id=org.isoron.uhabits&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-AC-global-none-all-co-pr-py-PartBadges-Oct1515-1"><img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" height="80px"/></a>
|
||||
<a href="https://f-droid.org/app/org.isoron.uhabits"><img alt="Get it on F-Droid" src="https://f-droid.org/badge/get-it-on.png" height="80px"/></a>
|
||||
</p>
|
||||
|
||||
## Screenshots
|
||||
@@ -63,8 +74,10 @@ contribute, even if you are not a software developer.
|
||||
|
||||
* **Report bugs, suggest features.** The easiest way to contribute is to simply
|
||||
use the app and let us know if you find any problems or have any suggestions
|
||||
to improve it. You can either use the link inside the app, or open an issue
|
||||
at GitHub. If you would like to receive the newest versions of the app
|
||||
to improve it. To report a problem, please [create a new bug report](https://github.com/iSoron/uhabits/issues/new/choose).
|
||||
To request a new feature or vote on existing feature requests, please visit
|
||||
our [GitHub Discussions page](https://github.com/iSoron/uhabits/discussions/categories/feature-requests).
|
||||
If you would like to receive the newest versions of the app
|
||||
earlier than everyone else, [join our open beta on Google Play][beta].
|
||||
|
||||
* **Spread the word.** If you like the app, share it with your family, friends
|
||||
@@ -77,13 +90,13 @@ contribute, even if you are not a software developer.
|
||||
is already completed, you are also very welcome to join and proofread it.
|
||||
|
||||
* **Write some code.** If you are an Android developer, you are very welcome to
|
||||
contribute with code. Please, see the [developer guidelines][dev-guide] for more details.
|
||||
contribute with code. Please see the [guidelines](https://github.com/iSoron/uhabits/blob/dev/docs/GUIDELINES.md).
|
||||
|
||||
## License
|
||||
|
||||
<img align="right" src="https://www.gnu.org/graphics/gplv3-88x31.png">
|
||||
|
||||
Copyright (C) 2016-2019 Álinson Santos Xavier <isoron@gmail.com>
|
||||
Copyright (C) 2016-2021 Álinson Santos Xavier <isoron@gmail.com>
|
||||
|
||||
Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by the
|
||||
@@ -96,24 +109,23 @@ contribute, even if you are not a software developer.
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
[screen1]: screenshots/uhabits1.png
|
||||
[screen2]: screenshots/uhabits2.png
|
||||
[screen3]: screenshots/uhabits3.png
|
||||
[screen4]: screenshots/uhabits4.png
|
||||
[screen5]: screenshots/uhabits5.png
|
||||
[screen6]: screenshots/uhabits6.png
|
||||
[screen1th]: screenshots/uhabits1_th.png
|
||||
[screen2th]: screenshots/uhabits2_th.png
|
||||
[screen3th]: screenshots/uhabits3_th.png
|
||||
[screen4th]: screenshots/uhabits4_th.png
|
||||
[screen5th]: screenshots/uhabits5_th.png
|
||||
[screen6th]: screenshots/uhabits6_th.png
|
||||
[poedit]: http://translate.loophabits.org
|
||||
[screen1]: screenshots/1.png
|
||||
[screen2]: screenshots/2.png
|
||||
[screen3]: screenshots/3.png
|
||||
[screen4]: screenshots/4.png
|
||||
[screen5]: screenshots/5.png
|
||||
[screen6]: screenshots/6.png
|
||||
[screen1th]: screenshots/1.thumb.png
|
||||
[screen2th]: screenshots/2.thumb.png
|
||||
[screen3th]: screenshots/3.thumb.png
|
||||
[screen4th]: screenshots/4.thumb.png
|
||||
[screen5th]: screenshots/5.thumb.png
|
||||
[screen6th]: screenshots/6.thumb.png
|
||||
[poedit]: https://translate.loophabits.org
|
||||
[playstore]: https://play.google.com/store/apps/details?id=org.isoron.uhabits
|
||||
[releases]: https://github.com/iSoron/uhabits/releases
|
||||
[fdroid]: http://f-droid.org/app/org.isoron.uhabits
|
||||
[dev-guide]: https://github.com/iSoron/uhabits/wiki/Developer-guidelines
|
||||
[build]: https://github.com/iSoron/uhabits/wiki/Developer-guidelines#building
|
||||
[fdroid]: https://f-droid.org/app/org.isoron.uhabits
|
||||
[build]: https://github.com/iSoron/uhabits/blob/dev/docs/BUILD.md
|
||||
[beta]: https://play.google.com/apps/testing/org.isoron.uhabits
|
||||
|
||||
1
android/android-base/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -1,31 +0,0 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion COMPILE_SDK_VERSION as Integer
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion MIN_SDK_VERSION as Integer
|
||||
targetSdkVersion TARGET_SDK_VERSION as Integer
|
||||
versionCode VERSION_CODE as Integer
|
||||
versionName "$VERSION_NAME"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "com.google.dagger:dagger:$DAGGER_VERSION"
|
||||
implementation 'com.google.android.material:material:1.0.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||
implementation "org.apache.commons:commons-lang3:3.5"
|
||||
|
||||
annotationProcessor "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
|
||||
}
|
||||
25
android/android-base/proguard-rules.pro
vendored
@@ -1,25 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /gemini-b/opt/android-sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -1,2 +0,0 @@
|
||||
<manifest package="org.isoron.androidbase"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"/>
|
||||
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase;
|
||||
|
||||
import android.content.*;
|
||||
import android.os.*;
|
||||
import android.view.*;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
public class AndroidBugReporter
|
||||
{
|
||||
private final Context context;
|
||||
|
||||
@Inject
|
||||
public AndroidBugReporter(@NonNull @AppContext Context context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures and returns a bug report. The bug report contains some device
|
||||
* information and the logcat.
|
||||
*
|
||||
* @return a String containing the bug report.
|
||||
* @throws IOException when any I/O error occur.
|
||||
*/
|
||||
@NonNull
|
||||
public String getBugReport() throws IOException
|
||||
{
|
||||
String logcat = getLogcat();
|
||||
String deviceInfo = getDeviceInfo();
|
||||
|
||||
String log = "---------- BUG REPORT BEGINS ----------\n";
|
||||
log += deviceInfo + "\n" + logcat;
|
||||
log += "---------- BUG REPORT ENDS ------------\n";
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
public String getDeviceInfo()
|
||||
{
|
||||
if (context == null) return "null context\n";
|
||||
|
||||
WindowManager wm =
|
||||
(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
return
|
||||
String.format("App Version Name: %s\n", BuildConfig.VERSION_NAME) +
|
||||
String.format("App Version Code: %s\n", BuildConfig.VERSION_CODE) +
|
||||
String.format("OS Version: %s (%s)\n",
|
||||
System.getProperty("os.version"), Build.VERSION.INCREMENTAL) +
|
||||
String.format("OS API Level: %s\n", Build.VERSION.SDK) +
|
||||
String.format("Device: %s\n", Build.DEVICE) +
|
||||
String.format("Model (Product): %s (%s)\n", Build.MODEL,
|
||||
Build.PRODUCT) +
|
||||
String.format("Manufacturer: %s\n", Build.MANUFACTURER) +
|
||||
String.format("Other tags: %s\n", Build.TAGS) +
|
||||
String.format("Screen Width: %s\n",
|
||||
wm.getDefaultDisplay().getWidth()) +
|
||||
String.format("Screen Height: %s\n",
|
||||
wm.getDefaultDisplay().getHeight()) +
|
||||
String.format("External storage state: %s\n\n",
|
||||
Environment.getExternalStorageState());
|
||||
}
|
||||
|
||||
public String getLogcat() throws IOException
|
||||
{
|
||||
int maxLineCount = 250;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
String[] command = new String[]{ "logcat", "-d" };
|
||||
java.lang.Process process = Runtime.getRuntime().exec(command);
|
||||
|
||||
InputStreamReader in = new InputStreamReader(process.getInputStream());
|
||||
BufferedReader bufferedReader = new BufferedReader(in);
|
||||
|
||||
LinkedList<String> log = new LinkedList<>();
|
||||
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null)
|
||||
{
|
||||
log.addLast(line);
|
||||
if (log.size() > maxLineCount) log.removeFirst();
|
||||
}
|
||||
|
||||
for (String l : log)
|
||||
{
|
||||
builder.append(l);
|
||||
builder.append('\n');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures a bug report and saves it to a file in the SD card.
|
||||
* <p>
|
||||
* The contents of the file are generated by the method {@link
|
||||
* #getBugReport()}. The file is saved in the apps's external private
|
||||
* storage.
|
||||
*
|
||||
* @return the generated file.
|
||||
* @throws IOException when I/O errors occur.
|
||||
*/
|
||||
@NonNull
|
||||
public void dumpBugReportToFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
String date =
|
||||
new SimpleDateFormat("yyyy-MM-dd HHmmss", Locale.US).format(
|
||||
new Date());
|
||||
|
||||
if (context == null) throw new IllegalStateException();
|
||||
|
||||
File dir = new AndroidDirFinder(context).getFilesDir("Logs");
|
||||
if (dir == null)
|
||||
throw new IOException("log dir should not be null");
|
||||
|
||||
File logFile =
|
||||
new File(String.format("%s/Log %s.txt", dir.getPath(), date));
|
||||
FileWriter output = new FileWriter(logFile);
|
||||
output.write(getBugReport());
|
||||
output.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase;
|
||||
|
||||
import android.content.*;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.*;
|
||||
import android.util.*;
|
||||
|
||||
import org.isoron.androidbase.utils.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
public class AndroidDirFinder
|
||||
{
|
||||
@NonNull
|
||||
private Context context;
|
||||
|
||||
@Inject
|
||||
public AndroidDirFinder(@NonNull @AppContext Context context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public File getFilesDir(@Nullable String relativePath)
|
||||
{
|
||||
File externalFilesDirs[] =
|
||||
ContextCompat.getExternalFilesDirs(context, null);
|
||||
if (externalFilesDirs == null)
|
||||
{
|
||||
Log.e("BaseSystem",
|
||||
"getFilesDir: getExternalFilesDirs returned null");
|
||||
return null;
|
||||
}
|
||||
|
||||
return FileUtils.getDir(externalFilesDirs, relativePath);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.isoron.androidbase.activities.*;
|
||||
|
||||
public class BaseExceptionHandler implements Thread.UncaughtExceptionHandler
|
||||
{
|
||||
@Nullable
|
||||
private Thread.UncaughtExceptionHandler originalHandler;
|
||||
|
||||
@NonNull
|
||||
private BaseActivity activity;
|
||||
|
||||
public BaseExceptionHandler(@NonNull BaseActivity activity)
|
||||
{
|
||||
this.activity = activity;
|
||||
originalHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(@Nullable Thread thread,
|
||||
@Nullable Throwable ex)
|
||||
{
|
||||
if (ex == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
ex.printStackTrace();
|
||||
new AndroidBugReporter(activity).dumpBugReportToFile();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// if (ex.getCause() instanceof InconsistentDatabaseException)
|
||||
// {
|
||||
// HabitsApplication app = (HabitsApplication) activity.getApplication();
|
||||
// HabitList habits = app.getComponent().getHabitList();
|
||||
// habits.repair();
|
||||
// System.exit(0);
|
||||
// }
|
||||
|
||||
if (originalHandler != null)
|
||||
originalHandler.uncaughtException(thread, ex);
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase;
|
||||
|
||||
import android.content.*;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.*;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.*;
|
||||
|
||||
import javax.inject.*;
|
||||
import javax.net.ssl.*;
|
||||
|
||||
public class SSLContextProvider
|
||||
{
|
||||
private Context context;
|
||||
|
||||
@Inject
|
||||
public SSLContextProvider(@NonNull @AppContext Context context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public SSLContext getCACertSSLContext()
|
||||
{
|
||||
try
|
||||
{
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
InputStream caInput = context.getAssets().open("cacert.pem");
|
||||
Certificate ca = cf.generateCertificate(caInput);
|
||||
|
||||
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
ks.load(null, null);
|
||||
ks.setCertificateEntry("ca", ca);
|
||||
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
|
||||
TrustManagerFactory.getDefaultAlgorithm());
|
||||
tmf.init(ks);
|
||||
|
||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||
ctx.init(null, tmf.getTrustManagers(), null);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase.activities;
|
||||
|
||||
import android.content.*;
|
||||
import android.os.*;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.*;
|
||||
import android.view.*;
|
||||
|
||||
import org.isoron.androidbase.*;
|
||||
|
||||
import static android.R.anim.fade_in;
|
||||
import static android.R.anim.fade_out;
|
||||
|
||||
/**
|
||||
* Base class for all activities in the application.
|
||||
* <p>
|
||||
* This class delegates the responsibilities of an Android activity to other
|
||||
* classes. For example, callbacks related to menus are forwarded to a {@link
|
||||
* BaseMenu}, while callbacks related to activity results are forwarded to a
|
||||
* {@link BaseScreen}.
|
||||
* <p>
|
||||
* A BaseActivity also installs an {@link java.lang.Thread.UncaughtExceptionHandler}
|
||||
* to the main thread. By default, this handler is an instance of
|
||||
* BaseExceptionHandler, which logs the exception to the disk before the application
|
||||
* crashes. To the default handler, you should override the method
|
||||
* getExceptionHandler.
|
||||
*/
|
||||
abstract public class BaseActivity extends AppCompatActivity
|
||||
{
|
||||
@Nullable
|
||||
private BaseMenu baseMenu;
|
||||
|
||||
@Nullable
|
||||
private BaseScreen screen;
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(@Nullable Menu menu)
|
||||
{
|
||||
if (menu == null) return true;
|
||||
if (baseMenu == null) return true;
|
||||
baseMenu.onCreate(getMenuInflater(), menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@Nullable MenuItem item)
|
||||
{
|
||||
if (item == null) return false;
|
||||
if (baseMenu == null) return false;
|
||||
return baseMenu.onItemSelected(item);
|
||||
}
|
||||
|
||||
public void restartWithFade(Class<?> cls)
|
||||
{
|
||||
new Handler().postDelayed(() ->
|
||||
{
|
||||
finish();
|
||||
overridePendingTransition(fade_in, fade_out);
|
||||
startActivity(new Intent(this, cls));
|
||||
|
||||
}, 500); // HACK: Let the menu disappear first
|
||||
}
|
||||
|
||||
public void setBaseMenu(@Nullable BaseMenu baseMenu)
|
||||
{
|
||||
this.baseMenu = baseMenu;
|
||||
}
|
||||
|
||||
public void setScreen(@Nullable BaseScreen screen)
|
||||
{
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
public void showDialog(AppCompatDialogFragment dialog, String tag)
|
||||
{
|
||||
dialog.show(getSupportFragmentManager(), tag);
|
||||
}
|
||||
|
||||
public void showDialog(AppCompatDialog dialog)
|
||||
{
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int request, int result, Intent data)
|
||||
{
|
||||
if (screen == null) super.onActivityResult(request, result, data);
|
||||
else screen.onResult(request, result, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
Thread.setDefaultUncaughtExceptionHandler(getExceptionHandler());
|
||||
}
|
||||
|
||||
protected Thread.UncaughtExceptionHandler getExceptionHandler()
|
||||
{
|
||||
return new BaseExceptionHandler(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
if(screen != null) screen.reattachDialogs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivity(Intent intent)
|
||||
{
|
||||
try
|
||||
{
|
||||
super.startActivity(intent);
|
||||
}
|
||||
catch (ActivityNotFoundException e)
|
||||
{
|
||||
if (this.screen != null)
|
||||
this.screen.showMessage(R.string.activity_not_found);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase.activities;
|
||||
|
||||
import android.view.*;
|
||||
|
||||
import androidx.annotation.MenuRes;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Base class for all the menus in the application.
|
||||
* <p>
|
||||
* This class receives from BaseActivity all callbacks related to menus, such as
|
||||
* menu creation and click events. It also handles some implementation details
|
||||
* of creating menus in Android, such as inflating the resources.
|
||||
*/
|
||||
public abstract class BaseMenu
|
||||
{
|
||||
@NonNull
|
||||
private final BaseActivity activity;
|
||||
|
||||
public BaseMenu(@NonNull BaseActivity activity)
|
||||
{
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public BaseActivity getActivity()
|
||||
{
|
||||
return activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare that the menu has changed, and should be recreated.
|
||||
*/
|
||||
public void invalidate()
|
||||
{
|
||||
activity.invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the menu is first displayed.
|
||||
* <p>
|
||||
* The given menu is already inflated and ready to receive items. The
|
||||
* application should override this method and add items to the menu here.
|
||||
*
|
||||
* @param menu the menu that is being created.
|
||||
*/
|
||||
public void onCreate(@NonNull Menu menu)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the menu is first displayed.
|
||||
* <p>
|
||||
* This method should not be overridden. The application should override
|
||||
* the methods onCreate(Menu) and getMenuResourceId instead.
|
||||
*
|
||||
* @param inflater a menu inflater, for creating the menu
|
||||
* @param menu the menu that is being created.
|
||||
*/
|
||||
public void onCreate(@NonNull MenuInflater inflater, @NonNull Menu menu)
|
||||
{
|
||||
menu.clear();
|
||||
inflater.inflate(getMenuResourceId(), menu);
|
||||
onCreate(menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever an item on the menu is selected.
|
||||
*
|
||||
* @param item the item that was selected.
|
||||
* @return true if the event was consumed, or false otherwise
|
||||
*/
|
||||
public boolean onItemSelected(@NonNull MenuItem item)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the resource that should be used to inflate this menu.
|
||||
*
|
||||
* @return id of the menu resource.
|
||||
*/
|
||||
@MenuRes
|
||||
protected abstract int getMenuResourceId();
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase.activities;
|
||||
|
||||
import android.content.*;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.androidbase.*;
|
||||
import org.isoron.androidbase.utils.*;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.Build.VERSION_CODES.LOLLIPOP;
|
||||
|
||||
/**
|
||||
* Base class for all root views in the application.
|
||||
* <p>
|
||||
* A root view is an Android view that is directly attached to an activity. This
|
||||
* view usually includes a toolbar and a progress bar. This abstract class hides
|
||||
* some of the complexity of setting these things up, for every version of
|
||||
* Android.
|
||||
*/
|
||||
public abstract class BaseRootView extends FrameLayout
|
||||
{
|
||||
@NonNull
|
||||
private final Context context;
|
||||
|
||||
protected boolean shouldDisplayHomeAsUp = false;
|
||||
|
||||
@Nullable
|
||||
private BaseScreen screen;
|
||||
|
||||
public BaseRootView(@NonNull Context context)
|
||||
{
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public boolean getDisplayHomeAsUp()
|
||||
{
|
||||
return shouldDisplayHomeAsUp;
|
||||
}
|
||||
|
||||
public void setDisplayHomeAsUp(boolean b)
|
||||
{
|
||||
shouldDisplayHomeAsUp = b;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Toolbar getToolbar()
|
||||
{
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
if (toolbar == null) throw new RuntimeException(
|
||||
"Your BaseRootView should have a " +
|
||||
"toolbar with id R.id.toolbar");
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
public int getToolbarColor()
|
||||
{
|
||||
StyledResources res = new StyledResources(context);
|
||||
return res.getColor(R.attr.colorPrimary);
|
||||
}
|
||||
|
||||
protected void initToolbar()
|
||||
{
|
||||
if (SDK_INT >= LOLLIPOP)
|
||||
{
|
||||
getToolbar().setElevation(InterfaceUtils.dpToPixels(context, 2));
|
||||
|
||||
View view = findViewById(R.id.toolbarShadow);
|
||||
if (view != null) view.setVisibility(GONE);
|
||||
|
||||
view = findViewById(R.id.headerShadow);
|
||||
if (view != null) view.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void onAttachedToScreen(BaseScreen screen)
|
||||
{
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BaseScreen getScreen()
|
||||
{
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase.activities;
|
||||
|
||||
import android.content.*;
|
||||
import android.graphics.*;
|
||||
import android.graphics.drawable.*;
|
||||
import android.net.*;
|
||||
import android.os.*;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.content.res.*;
|
||||
import androidx.appcompat.app.*;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import org.isoron.androidbase.*;
|
||||
import org.isoron.androidbase.utils.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.Build.VERSION_CODES.LOLLIPOP;
|
||||
import static androidx.core.content.FileProvider.getUriForFile;
|
||||
|
||||
/**
|
||||
* Base class for all screens in the application.
|
||||
* <p>
|
||||
* Screens are responsible for deciding what root views and what menus should be
|
||||
* attached to the main window. They are also responsible for showing other
|
||||
* screens and for receiving their results.
|
||||
*/
|
||||
public class BaseScreen
|
||||
{
|
||||
protected BaseActivity activity;
|
||||
|
||||
@Nullable
|
||||
private BaseRootView rootView;
|
||||
|
||||
@Nullable
|
||||
private BaseSelectionMenu selectionMenu;
|
||||
|
||||
protected Snackbar snackbar;
|
||||
|
||||
public BaseScreen(@NonNull BaseActivity activity)
|
||||
{
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static int getDefaultActionBarColor(Context context)
|
||||
{
|
||||
if (SDK_INT < LOLLIPOP)
|
||||
{
|
||||
return ResourcesCompat.getColor(context.getResources(),
|
||||
R.color.grey_900, context.getTheme());
|
||||
}
|
||||
else
|
||||
{
|
||||
StyledResources res = new StyledResources(context);
|
||||
return res.getColor(R.attr.colorPrimary);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void setupActionBarColor(@NonNull AppCompatActivity activity,
|
||||
int color)
|
||||
{
|
||||
|
||||
Toolbar toolbar = (Toolbar) activity.findViewById(R.id.toolbar);
|
||||
if (toolbar == null) return;
|
||||
|
||||
activity.setSupportActionBar(toolbar);
|
||||
|
||||
ActionBar actionBar = activity.getSupportActionBar();
|
||||
if (actionBar == null) return;
|
||||
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
ColorDrawable drawable = new ColorDrawable(color);
|
||||
actionBar.setBackgroundDrawable(drawable);
|
||||
|
||||
if (SDK_INT >= LOLLIPOP)
|
||||
{
|
||||
int darkerColor = ColorUtils.mixColors(color, Color.BLACK, 0.75f);
|
||||
activity.getWindow().setStatusBarColor(darkerColor);
|
||||
|
||||
toolbar.setElevation(InterfaceUtils.dpToPixels(activity, 2));
|
||||
|
||||
View view = activity.findViewById(R.id.toolbarShadow);
|
||||
if (view != null) view.setVisibility(View.GONE);
|
||||
|
||||
view = activity.findViewById(R.id.headerShadow);
|
||||
if (view != null) view.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the screen that its contents should be updated.
|
||||
*/
|
||||
public void invalidate()
|
||||
{
|
||||
if (rootView == null) return;
|
||||
rootView.invalidate();
|
||||
}
|
||||
|
||||
public void invalidateToolbar()
|
||||
{
|
||||
if (rootView == null) return;
|
||||
|
||||
activity.runOnUiThread(() ->
|
||||
{
|
||||
Toolbar toolbar = rootView.getToolbar();
|
||||
activity.setSupportActionBar(toolbar);
|
||||
ActionBar actionBar = activity.getSupportActionBar();
|
||||
if (actionBar == null) return;
|
||||
|
||||
actionBar.setDisplayHomeAsUpEnabled(rootView.getDisplayHomeAsUp());
|
||||
|
||||
int color = rootView.getToolbarColor();
|
||||
setActionBarColor(actionBar, color);
|
||||
setStatusBarColor(color);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when another Activity has finished, and has returned some result.
|
||||
*
|
||||
* @param requestCode the request code originally supplied to {@link
|
||||
* android.app.Activity#startActivityForResult(Intent,
|
||||
* int, Bundle)}.
|
||||
* @param resultCode the result code sent by the other activity.
|
||||
* @param data an Intent containing extra data sent by the other
|
||||
* activity.
|
||||
* @see {@link android.app.Activity#onActivityResult(int, int, Intent)}
|
||||
*/
|
||||
public void onResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called after activity has been recreated, and the dialogs should be
|
||||
* reattached to their controllers.
|
||||
*/
|
||||
public void reattachDialogs()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the menu to be shown by this screen.
|
||||
* <p>
|
||||
* This menu will be visible if when there is no active selection operation.
|
||||
* If the provided menu is null, then no menu will be shown.
|
||||
*
|
||||
* @param menu the menu to be shown.
|
||||
*/
|
||||
public void setMenu(@Nullable BaseMenu menu)
|
||||
{
|
||||
activity.setBaseMenu(menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the root view for this screen.
|
||||
*
|
||||
* @param rootView the root view for this screen.
|
||||
*/
|
||||
public void setRootView(@Nullable BaseRootView rootView)
|
||||
{
|
||||
this.rootView = rootView;
|
||||
activity.setContentView(rootView);
|
||||
if (rootView == null) return;
|
||||
rootView.onAttachedToScreen(this);
|
||||
invalidateToolbar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the menu to be shown when a selection is active on the screen.
|
||||
*
|
||||
* @param menu the menu to be shown during a selection
|
||||
*/
|
||||
public void setSelectionMenu(@Nullable BaseSelectionMenu menu)
|
||||
{
|
||||
this.selectionMenu = menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a message on the screen.
|
||||
*
|
||||
* @param stringId the string resource id for this message.
|
||||
*/
|
||||
public void showMessage(@StringRes Integer stringId)
|
||||
{
|
||||
if (stringId == null || rootView == null) return;
|
||||
if (snackbar == null)
|
||||
{
|
||||
snackbar = Snackbar.make(rootView, stringId, Snackbar.LENGTH_SHORT);
|
||||
int tvId = R.id.snackbar_text;
|
||||
TextView tv = (TextView) snackbar.getView().findViewById(tvId);
|
||||
tv.setTextColor(Color.WHITE);
|
||||
}
|
||||
else snackbar.setText(stringId);
|
||||
snackbar.show();
|
||||
}
|
||||
|
||||
public void showSendEmailScreen(@StringRes int toId,
|
||||
@StringRes int subjectId,
|
||||
String content)
|
||||
{
|
||||
String to = activity.getString(toId);
|
||||
String subject = activity.getString(subjectId);
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.setType("message/rfc822");
|
||||
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{ to });
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, content);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
public void showSendFileScreen(@NonNull String archiveFilename)
|
||||
{
|
||||
File file = new File(archiveFilename);
|
||||
Uri fileUri = getUriForFile(activity, "org.isoron.uhabits", file);
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.setType("application/zip");
|
||||
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the screen to start a selection.
|
||||
* <p>
|
||||
* If a selection menu was provided, this menu will be shown instead of the
|
||||
* regular one.
|
||||
*/
|
||||
public void startSelection()
|
||||
{
|
||||
activity.startSupportActionMode(new ActionModeWrapper());
|
||||
}
|
||||
|
||||
private void setActionBarColor(@NonNull ActionBar actionBar, int color)
|
||||
{
|
||||
ColorDrawable drawable = new ColorDrawable(color);
|
||||
actionBar.setBackgroundDrawable(drawable);
|
||||
}
|
||||
|
||||
private void setStatusBarColor(int baseColor)
|
||||
{
|
||||
if (SDK_INT < LOLLIPOP) return;
|
||||
|
||||
int darkerColor = ColorUtils.mixColors(baseColor, Color.BLACK, 0.75f);
|
||||
activity.getWindow().setStatusBarColor(darkerColor);
|
||||
}
|
||||
|
||||
private class ActionModeWrapper implements ActionMode.Callback
|
||||
{
|
||||
@Override
|
||||
public boolean onActionItemClicked(@Nullable ActionMode mode,
|
||||
@Nullable MenuItem item)
|
||||
{
|
||||
if (item == null || selectionMenu == null) return false;
|
||||
return selectionMenu.onItemClicked(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(@Nullable ActionMode mode,
|
||||
@Nullable Menu menu)
|
||||
{
|
||||
if (selectionMenu == null) return false;
|
||||
if (mode == null || menu == null) return false;
|
||||
selectionMenu.onCreate(activity.getMenuInflater(), mode, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(@Nullable ActionMode mode)
|
||||
{
|
||||
if (selectionMenu == null) return;
|
||||
selectionMenu.onFinish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(@Nullable ActionMode mode,
|
||||
@Nullable Menu menu)
|
||||
{
|
||||
if (selectionMenu == null || menu == null) return false;
|
||||
return selectionMenu.onPrepare(menu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase.activities;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import android.view.*;
|
||||
|
||||
/**
|
||||
* Base class for all the selection menus in the application.
|
||||
* <p>
|
||||
* A selection menu is a menu that appears when the screen starts a selection
|
||||
* operation. It contains actions that modify the selected items, such as delete
|
||||
* or archive. Since it replaces the toolbar, it also has a title.
|
||||
* <p>
|
||||
* This class hides many implementation details of creating such menus in
|
||||
* Android. The interface is supposed to look very similar to {@link BaseMenu},
|
||||
* with a few additional methods, such as finishing the selection operation.
|
||||
* Internally, it uses an {@link ActionMode}.
|
||||
*/
|
||||
public abstract class BaseSelectionMenu
|
||||
{
|
||||
@Nullable
|
||||
private ActionMode actionMode;
|
||||
|
||||
/**
|
||||
* Finishes the selection operation.
|
||||
*/
|
||||
public void finish()
|
||||
{
|
||||
if (actionMode != null) actionMode.finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare that the menu has changed, and should be recreated.
|
||||
*/
|
||||
public void invalidate()
|
||||
{
|
||||
if (actionMode != null) actionMode.invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the menu is first displayed.
|
||||
* <p>
|
||||
* This method should not be overridden. The application should override
|
||||
* the methods onCreate(Menu) and getMenuResourceId instead.
|
||||
*
|
||||
* @param inflater a menu inflater, for creating the menu
|
||||
* @param mode the action mode associated with this menu.
|
||||
* @param menu the menu that is being created.
|
||||
*/
|
||||
public void onCreate(@NonNull MenuInflater inflater,
|
||||
@NonNull ActionMode mode,
|
||||
@NonNull Menu menu)
|
||||
{
|
||||
this.actionMode = mode;
|
||||
inflater.inflate(getResourceId(), menu);
|
||||
onCreate(menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the selection operation is about to finish.
|
||||
*/
|
||||
public void onFinish()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever an item on the menu is selected.
|
||||
*
|
||||
* @param item the item that was selected.
|
||||
* @return true if the event was consumed, or false otherwise
|
||||
*/
|
||||
public boolean onItemClicked(@NonNull MenuItem item)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called whenever the menu is invalidated.
|
||||
*
|
||||
* @param menu the menu to be refreshed
|
||||
* @return true if the menu has changes, false otherwise
|
||||
*/
|
||||
public boolean onPrepare(@NonNull Menu menu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title of the selection menu.
|
||||
*
|
||||
* @param title the new title.
|
||||
*/
|
||||
public void setTitle(String title)
|
||||
{
|
||||
if (actionMode != null) actionMode.setTitle(title);
|
||||
}
|
||||
|
||||
protected abstract int getResourceId();
|
||||
|
||||
/**
|
||||
* Called when the menu is first created.
|
||||
*
|
||||
* @param menu the menu being created
|
||||
*/
|
||||
protected void onCreate(@NonNull Menu menu)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase.utils;
|
||||
|
||||
import android.graphics.*;
|
||||
|
||||
public abstract class ColorUtils
|
||||
{
|
||||
public static int mixColors(int color1, int color2, float amount)
|
||||
{
|
||||
final byte ALPHA_CHANNEL = 24;
|
||||
final byte RED_CHANNEL = 16;
|
||||
final byte GREEN_CHANNEL = 8;
|
||||
final byte BLUE_CHANNEL = 0;
|
||||
|
||||
final float inverseAmount = 1.0f - amount;
|
||||
|
||||
int a = ((int) (((float) (color1 >> ALPHA_CHANNEL & 0xff) * amount) +
|
||||
((float) (color2 >> ALPHA_CHANNEL & 0xff) *
|
||||
inverseAmount))) & 0xff;
|
||||
int r = ((int) (((float) (color1 >> RED_CHANNEL & 0xff) * amount) +
|
||||
((float) (color2 >> RED_CHANNEL & 0xff) *
|
||||
inverseAmount))) & 0xff;
|
||||
int g = ((int) (((float) (color1 >> GREEN_CHANNEL & 0xff) * amount) +
|
||||
((float) (color2 >> GREEN_CHANNEL & 0xff) *
|
||||
inverseAmount))) & 0xff;
|
||||
int b = ((int) (((float) (color1 & 0xff) * amount) +
|
||||
((float) (color2 & 0xff) * inverseAmount))) & 0xff;
|
||||
|
||||
return a << ALPHA_CHANNEL | r << RED_CHANNEL | g << GREEN_CHANNEL |
|
||||
b << BLUE_CHANNEL;
|
||||
}
|
||||
|
||||
public static int setAlpha(int color, float newAlpha)
|
||||
{
|
||||
int intAlpha = (int) (newAlpha * 255);
|
||||
return Color.argb(intAlpha, Color.red(color), Color.green(color),
|
||||
Color.blue(color));
|
||||
}
|
||||
|
||||
public static int setMinValue(int color, float newValue)
|
||||
{
|
||||
float hsv[] = new float[3];
|
||||
Color.colorToHSV(color, hsv);
|
||||
hsv[2] = Math.max(hsv[2], newValue);
|
||||
return Color.HSVToColor(hsv);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase.utils;
|
||||
|
||||
import android.os.*;
|
||||
import android.util.*;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public abstract class FileUtils
|
||||
{
|
||||
public static void copy(File src, File dst) throws IOException
|
||||
{
|
||||
FileInputStream inStream = new FileInputStream(src);
|
||||
FileOutputStream outStream = new FileOutputStream(dst);
|
||||
copy(inStream, outStream);
|
||||
}
|
||||
|
||||
public static void copy(InputStream inStream, File dst) throws IOException
|
||||
{
|
||||
FileOutputStream outStream = new FileOutputStream(dst);
|
||||
copy(inStream, outStream);
|
||||
}
|
||||
|
||||
public static void copy(InputStream in, OutputStream out) throws IOException
|
||||
{
|
||||
int numBytes;
|
||||
byte[] buffer = new byte[1024];
|
||||
|
||||
while ((numBytes = in.read(buffer)) != -1)
|
||||
out.write(buffer, 0, numBytes);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getDir(@NonNull File potentialParentDirs[],
|
||||
@Nullable String relativePath)
|
||||
{
|
||||
if (relativePath == null) relativePath = "";
|
||||
|
||||
File chosenDir = null;
|
||||
for (File dir : potentialParentDirs)
|
||||
{
|
||||
if (dir == null || !dir.canWrite()) continue;
|
||||
chosenDir = dir;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chosenDir == null)
|
||||
{
|
||||
Log.e("FileUtils",
|
||||
"getDir: all potential parents are null or non-writable");
|
||||
return null;
|
||||
}
|
||||
|
||||
File dir = new File(
|
||||
String.format("%s/%s/", chosenDir.getAbsolutePath(), relativePath));
|
||||
if (!dir.exists() && !dir.mkdirs())
|
||||
{
|
||||
Log.e("FileUtils",
|
||||
"getDir: chosen dir does not exist and cannot be created");
|
||||
return null;
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getSDCardDir(@Nullable String relativePath)
|
||||
{
|
||||
File parents[] =
|
||||
new File[]{ Environment.getExternalStorageDirectory() };
|
||||
return getDir(parents, relativePath);
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase.utils;
|
||||
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.graphics.*;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.view.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
|
||||
public abstract class InterfaceUtils
|
||||
{
|
||||
private static Typeface fontAwesome;
|
||||
|
||||
@Nullable
|
||||
private static Float fixedResolution = null;
|
||||
|
||||
public static void setFixedResolution(@NonNull Float f)
|
||||
{
|
||||
fixedResolution = f;
|
||||
}
|
||||
|
||||
public static Typeface getFontAwesome(Context context)
|
||||
{
|
||||
if(fontAwesome == null) fontAwesome =
|
||||
Typeface.createFromAsset(context.getAssets(),
|
||||
"fontawesome-webfont.ttf");
|
||||
|
||||
return fontAwesome;
|
||||
}
|
||||
|
||||
public static float dpToPixels(Context context, float dp)
|
||||
{
|
||||
if(fixedResolution != null) return dp * fixedResolution;
|
||||
|
||||
Resources resources = context.getResources();
|
||||
DisplayMetrics metrics = resources.getDisplayMetrics();
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);
|
||||
}
|
||||
|
||||
public static float spToPixels(Context context, float sp)
|
||||
{
|
||||
if(fixedResolution != null) return sp * fixedResolution;
|
||||
|
||||
Resources resources = context.getResources();
|
||||
DisplayMetrics metrics = resources.getDisplayMetrics();
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, metrics);
|
||||
}
|
||||
|
||||
public static float getDimension(Context context, int id)
|
||||
{
|
||||
float dim = context.getResources().getDimension(id);
|
||||
if (fixedResolution == null) return dim;
|
||||
else
|
||||
{
|
||||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
float actualDensity = dm.density;
|
||||
return dim / actualDensity * fixedResolution;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setupEditorAction(@NonNull ViewGroup parent,
|
||||
@NonNull TextView.OnEditorActionListener listener)
|
||||
{
|
||||
for (int i = 0; i < parent.getChildCount(); i++)
|
||||
{
|
||||
View child = parent.getChildAt(i);
|
||||
|
||||
if (child instanceof ViewGroup)
|
||||
setupEditorAction((ViewGroup) child, listener);
|
||||
|
||||
if (child instanceof TextView)
|
||||
((TextView) child).setOnEditorActionListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLayoutRtl(View view)
|
||||
{
|
||||
return ViewCompat.getLayoutDirection(view) ==
|
||||
ViewCompat.LAYOUT_DIRECTION_RTL;
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.androidbase.utils;
|
||||
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.graphics.drawable.*;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.isoron.androidbase.*;
|
||||
|
||||
public class StyledResources
|
||||
{
|
||||
private static Integer fixedTheme;
|
||||
|
||||
private final Context context;
|
||||
|
||||
public StyledResources(@NonNull Context context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public static void setFixedTheme(Integer theme)
|
||||
{
|
||||
fixedTheme = theme;
|
||||
}
|
||||
|
||||
public boolean getBoolean(@AttrRes int attrId)
|
||||
{
|
||||
TypedArray ta = getTypedArray(attrId);
|
||||
boolean bool = ta.getBoolean(0, false);
|
||||
ta.recycle();
|
||||
|
||||
return bool;
|
||||
}
|
||||
|
||||
public int getDimension(@AttrRes int attrId)
|
||||
{
|
||||
TypedArray ta = getTypedArray(attrId);
|
||||
int dim = ta.getDimensionPixelSize(0, 0);
|
||||
ta.recycle();
|
||||
|
||||
return dim;
|
||||
}
|
||||
|
||||
public int getColor(@AttrRes int attrId)
|
||||
{
|
||||
TypedArray ta = getTypedArray(attrId);
|
||||
int color = ta.getColor(0, 0);
|
||||
ta.recycle();
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
public Drawable getDrawable(@AttrRes int attrId)
|
||||
{
|
||||
TypedArray ta = getTypedArray(attrId);
|
||||
Drawable drawable = ta.getDrawable(0);
|
||||
ta.recycle();
|
||||
|
||||
return drawable;
|
||||
}
|
||||
|
||||
public float getFloat(@AttrRes int attrId)
|
||||
{
|
||||
TypedArray ta = getTypedArray(attrId);
|
||||
float f = ta.getFloat(0, 0);
|
||||
ta.recycle();
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
public int[] getPalette()
|
||||
{
|
||||
int resourceId = getResource(R.attr.palette);
|
||||
if (resourceId < 0) throw new RuntimeException("resource not found");
|
||||
|
||||
return context.getResources().getIntArray(resourceId);
|
||||
}
|
||||
|
||||
public int getResource(@AttrRes int attrId)
|
||||
{
|
||||
TypedArray ta = getTypedArray(attrId);
|
||||
int resourceId = ta.getResourceId(0, -1);
|
||||
ta.recycle();
|
||||
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
private TypedArray getTypedArray(@AttrRes int attrId)
|
||||
{
|
||||
int[] attrs = new int[]{ attrId };
|
||||
|
||||
if (fixedTheme != null)
|
||||
return context.getTheme().obtainStyledAttributes(fixedTheme, attrs);
|
||||
|
||||
return context.obtainStyledAttributes(attrs);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<resources>
|
||||
<item name="toolbar" type="id" />
|
||||
<item name="toolbarShadow" type="id" />
|
||||
<item name="headerShadow" type="id" />
|
||||
<attr name="palette" format="reference"/>
|
||||
</resources>
|
||||
1
android/android-pickers/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -1,24 +0,0 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion COMPILE_SDK_VERSION as Integer
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion MIN_SDK_VERSION as Integer
|
||||
targetSdkVersion TARGET_SDK_VERSION as Integer
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||
}
|
||||
25
android/android-pickers/proguard-rules.pro
vendored
@@ -1,25 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /gemini-b/opt/android-sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -1,2 +0,0 @@
|
||||
<manifest package="com.android"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"/>
|
||||
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="select_hours"/>
|
||||
<string name="select_minutes"/>
|
||||
<string name="color_picker_default_title"/>
|
||||
<string name="clear"/>
|
||||
<string name="clear_label"/>
|
||||
<string name="done_label"/>
|
||||
</resources>
|
||||
@@ -1,21 +0,0 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:$BUILD_TOOLS_VERSION"
|
||||
classpath "com.neenbedankt.gradle.plugins:android-apt:1.8"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION"
|
||||
classpath "org.ajoberstar:grgit:1.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
279
android/build.sh
@@ -1,279 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||
# This file is part of Loop Habit Tracker.
|
||||
#
|
||||
# Loop Habit Tracker 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 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# Loop Habit Tracker 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
ADB="${ANDROID_HOME}/platform-tools/adb"
|
||||
EMULATOR="${ANDROID_HOME}/tools/emulator"
|
||||
GRADLE="./gradlew --stacktrace"
|
||||
PACKAGE_NAME=org.isoron.uhabits
|
||||
OUTPUTS_DIR=uhabits-android/build/outputs
|
||||
VERSION=$(cat gradle.properties | grep VERSION_NAME | sed -e 's/.*=//g;s/ //g')
|
||||
|
||||
if [ ! -f "${ANDROID_HOME}/platform-tools/adb" ]; then
|
||||
echo "Error: ANDROID_HOME is not set correctly"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_error() {
|
||||
if [ ! -z "$TEAMCITY_VERSION" ]; then
|
||||
echo "###teamcity[progressMessage '$1']"
|
||||
else
|
||||
local COLOR='\033[1;31m'
|
||||
local NC='\033[0m'
|
||||
echo -e "$COLOR>>> $1 $NC"
|
||||
fi
|
||||
}
|
||||
|
||||
log_info() {
|
||||
if [ ! -z "$TEAMCITY_VERSION" ]; then
|
||||
echo "###teamcity[progressMessage '$1']"
|
||||
else
|
||||
local COLOR='\033[1;32m'
|
||||
local NC='\033[0m'
|
||||
echo -e "$COLOR>>> $1 $NC"
|
||||
fi
|
||||
}
|
||||
|
||||
fail() {
|
||||
log_error "BUILD FAILED"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ ! -z $RELEASE ]; then
|
||||
log_info "Reading secret env variables from ../.secret/env"
|
||||
source ../.secret/env || fail
|
||||
fi
|
||||
|
||||
|
||||
run_adb_as_root() {
|
||||
log_info "Running adb as root"
|
||||
$ADB root
|
||||
}
|
||||
|
||||
build_apk() {
|
||||
log_info "Removing old APKs..."
|
||||
rm -vf build/*.apk
|
||||
|
||||
if [ ! -z $RELEASE ]; then
|
||||
log_info "Building release APK"
|
||||
./gradlew assembleRelease
|
||||
cp -v uhabits-android/build/outputs/apk/release/uhabits-android-release.apk build/loop-$VERSION-release.apk
|
||||
fi
|
||||
|
||||
log_info "Building debug APK"
|
||||
./gradlew assembleDebug || fail
|
||||
cp -v uhabits-android/build/outputs/apk/debug/uhabits-android-debug.apk build/loop-$VERSION-debug.apk
|
||||
}
|
||||
|
||||
build_instrumentation_apk() {
|
||||
log_info "Building instrumentation APK"
|
||||
if [ ! -z $RELEASE ]; then
|
||||
$GRADLE assembleAndroidTest \
|
||||
-Pandroid.injected.signing.store.file=$LOOP_KEY_STORE \
|
||||
-Pandroid.injected.signing.store.password=$LOOP_STORE_PASSWORD \
|
||||
-Pandroid.injected.signing.key.alias=$LOOP_KEY_ALIAS \
|
||||
-Pandroid.injected.signing.key.password=$LOOP_KEY_PASSWORD || fail
|
||||
else
|
||||
$GRADLE assembleAndroidTest || fail
|
||||
fi
|
||||
}
|
||||
|
||||
uninstall_apk() {
|
||||
log_info "Uninstalling existing APK"
|
||||
$ADB uninstall ${PACKAGE_NAME}
|
||||
}
|
||||
|
||||
install_test_butler() {
|
||||
log_info "Installing Test Butler"
|
||||
$ADB uninstall com.linkedin.android.testbutler
|
||||
$ADB install tools/test-butler-app-2.0.2.apk
|
||||
}
|
||||
|
||||
install_apk() {
|
||||
log_info "Installing APK"
|
||||
if [ ! -z $RELEASE ]; then
|
||||
$ADB install -r ${OUTPUTS_DIR}/apk/release/uhabits-android-release.apk || fail
|
||||
else
|
||||
$ADB install -t -r ${OUTPUTS_DIR}/apk/debug/uhabits-android-debug.apk || fail
|
||||
fi
|
||||
}
|
||||
|
||||
install_test_apk() {
|
||||
log_info "Uninstalling existing test APK"
|
||||
$ADB uninstall ${PACKAGE_NAME}.test
|
||||
|
||||
log_info "Installing test APK"
|
||||
$ADB install -r ${OUTPUTS_DIR}/apk/androidTest/debug/uhabits-android-debug-androidTest.apk || fail
|
||||
}
|
||||
|
||||
run_instrumented_tests() {
|
||||
SIZE=$1
|
||||
log_info "Running instrumented tests"
|
||||
$ADB shell am instrument \
|
||||
-r -e coverage true -e size $SIZE \
|
||||
-w ${PACKAGE_NAME}.test/androidx.test.runner.AndroidJUnitRunner \
|
||||
| tee ${OUTPUTS_DIR}/instrument.txt
|
||||
|
||||
if grep "\(INSTRUMENTATION_STATUS_CODE.*-1\|FAILURES\)" $OUTPUTS_DIR/instrument.txt; then
|
||||
log_error "Some instrumented tests failed"
|
||||
fetch_images
|
||||
fetch_logcat
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#mkdir -p ${OUTPUTS_DIR}/code-coverage/connected/
|
||||
#$ADB pull /data/user/0/${PACKAGE_NAME}/files/coverage.ec \
|
||||
# ${OUTPUTS_DIR}/code-coverage/connected/ \
|
||||
# || log_error "COVERAGE REPORT NOT AVAILABLE"
|
||||
}
|
||||
|
||||
parse_instrumentation_results() {
|
||||
log_info "Parsing instrumented test results"
|
||||
java -jar tools/automator-log-converter-1.5.0.jar ${OUTPUTS_DIR}/instrument.txt || fail
|
||||
}
|
||||
|
||||
generate_coverage_badge() {
|
||||
log_info "Generating code coverage badge"
|
||||
CORE_REPORT=uhabits-core/build/reports/jacoco/test/jacocoTestReport.xml
|
||||
rm -f ${OUTPUTS_DIR}/coverage-badge.svg
|
||||
python3 tools/coverage-badge/badge.py -i $CORE_REPORT -o ${OUTPUTS_DIR}/coverage-badge
|
||||
}
|
||||
|
||||
fetch_logcat() {
|
||||
log_info "Fetching logcat"
|
||||
$ADB logcat -d > ${OUTPUTS_DIR}/logcat.txt
|
||||
}
|
||||
|
||||
run_jvm_tests() {
|
||||
log_info "Running JVM tests"
|
||||
if [ ! -z $RELEASE ]; then
|
||||
$GRADLE testReleaseUnitTest :uhabits-core:check || fail
|
||||
else
|
||||
$GRADLE testDebugUnitTest :uhabits-core:check || fail
|
||||
fi
|
||||
}
|
||||
|
||||
uninstall_test_apk() {
|
||||
log_info "Uninstalling test APK"
|
||||
$ADB uninstall ${PACKAGE_NAME}.test
|
||||
}
|
||||
|
||||
fetch_images() {
|
||||
log_info "Fetching images"
|
||||
rm -rf $OUTPUTS_DIR/test-screenshots
|
||||
$ADB pull /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots/ $OUTPUTS_DIR
|
||||
$ADB shell rm -r /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots/
|
||||
}
|
||||
|
||||
accept_images() {
|
||||
find $OUTPUTS_DIR/test-screenshots -name '*.expected*' -delete
|
||||
rsync -av $OUTPUTS_DIR/test-screenshots/ uhabits-android/src/androidTest/assets/
|
||||
}
|
||||
|
||||
run_tests() {
|
||||
SIZE=$1
|
||||
run_adb_as_root
|
||||
install_test_butler
|
||||
uninstall_apk
|
||||
install_apk
|
||||
install_test_apk
|
||||
run_instrumented_tests $SIZE
|
||||
parse_instrumentation_results
|
||||
fetch_logcat
|
||||
uninstall_test_apk
|
||||
}
|
||||
|
||||
parse_opts() {
|
||||
OPTS=`getopt -o r --long release -n 'build.sh' -- "$@"`
|
||||
if [ $? != 0 ] ; then exit 1; fi
|
||||
eval set -- "$OPTS"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-r | --release ) RELEASE=1; shift ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
remove_build_dir() {
|
||||
rm -rfv build
|
||||
rm -rfv android-base/build
|
||||
rm -rfv android-pickers/build
|
||||
rm -rfv uhabits-android/build
|
||||
rm -rfv uhabits-core/build
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
build)
|
||||
shift; parse_opts $*
|
||||
|
||||
build_apk
|
||||
build_instrumentation_apk
|
||||
run_jvm_tests
|
||||
#generate_coverage_badge
|
||||
;;
|
||||
|
||||
medium-tests)
|
||||
shift; parse_opts $*
|
||||
run_tests medium
|
||||
;;
|
||||
|
||||
large-tests)
|
||||
shift; parse_opts $*
|
||||
run_tests large
|
||||
;;
|
||||
|
||||
fetch-images)
|
||||
fetch_images
|
||||
;;
|
||||
|
||||
accept-images)
|
||||
accept_images
|
||||
;;
|
||||
|
||||
install)
|
||||
shift; parse_opts $*
|
||||
build_apk
|
||||
install_apk
|
||||
;;
|
||||
|
||||
clean)
|
||||
remove_build_dir
|
||||
;;
|
||||
|
||||
*)
|
||||
cat <<END
|
||||
Usage: $0 <command> [options]
|
||||
Builds, installs and tests Loop Habit Tracker
|
||||
|
||||
Commands:
|
||||
accept-images Copies fetched images to corresponding assets folder
|
||||
build Build APK and run JVM tests
|
||||
clean Remove build directory
|
||||
fetch-images Fetches failed view test images from device
|
||||
install Install app on connected device
|
||||
large-tests Run large-sized tests on connected device
|
||||
medium-tests Run medium-sized tests on connected device
|
||||
|
||||
Options:
|
||||
-r --release Build and test release APK, instead of debug
|
||||
END
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -1,18 +0,0 @@
|
||||
VERSION_CODE = 52
|
||||
VERSION_NAME = 1.8.9
|
||||
|
||||
MIN_SDK_VERSION = 21
|
||||
TARGET_SDK_VERSION = 29
|
||||
COMPILE_SDK_VERSION = 29
|
||||
|
||||
DAGGER_VERSION = 2.25.4
|
||||
KOTLIN_VERSION = 1.3.61
|
||||
SUPPORT_LIBRARY_VERSION = 28.0.0
|
||||
AUTO_FACTORY_VERSION = 1.0-beta6
|
||||
BUILD_TOOLS_VERSION = 3.5.3
|
||||
|
||||
org.gradle.parallel=false
|
||||
org.gradle.daemon=true
|
||||
org.gradle.jvmargs=-Xms2048m -Xmx2048m -XX:MaxPermSize=2048m
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
@@ -1 +0,0 @@
|
||||
uhabits-android/src/main/play/
|
||||
@@ -1 +0,0 @@
|
||||
include ':uhabits-android', ':uhabits-core', ':android-base', ':android-pickers'
|
||||
@@ -1,157 +0,0 @@
|
||||
"""
|
||||
Generate coverage badges for Coverage.py.
|
||||
Forked from https://github.com/dbrgn/coverage-badge
|
||||
"""
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import print_function, division, absolute_import, unicode_literals
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import pkg_resources
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
__version__ = '0.2.0-uhabits'
|
||||
|
||||
|
||||
DEFAULT_COLOR = '#a4a61d'
|
||||
COLORS = {
|
||||
'brightgreen': '#4c1',
|
||||
'green': '#97CA00',
|
||||
'yellowgreen': '#a4a61d',
|
||||
'yellow': '#dfb317',
|
||||
'orange': '#fe7d37',
|
||||
'red': '#e05d44',
|
||||
'lightgrey': '#9f9f9f',
|
||||
}
|
||||
|
||||
COLOR_RANGES = [
|
||||
(95, 'brightgreen'),
|
||||
(90, 'green'),
|
||||
(75, 'yellowgreen'),
|
||||
(60, 'yellow'),
|
||||
(40, 'orange'),
|
||||
(0, 'red'),
|
||||
]
|
||||
|
||||
|
||||
class Devnull(object):
|
||||
"""
|
||||
A file like object that does nothing.
|
||||
"""
|
||||
def write(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
def get_total(report):
|
||||
missed = 0
|
||||
covered = 0
|
||||
for r in report.split(":"):
|
||||
doc = BeautifulSoup(open(r), 'xml')
|
||||
tag = doc.select("report > counter[type^INST]")[0]
|
||||
missed = missed + float(tag['missed'])
|
||||
covered = covered + float(tag['covered'])
|
||||
return str(int(round(100 * covered / (missed + covered))))
|
||||
|
||||
def get_color(total):
|
||||
"""
|
||||
Return color for current coverage precent
|
||||
"""
|
||||
try:
|
||||
xtotal = int(total)
|
||||
except ValueError:
|
||||
return COLORS['lightgrey']
|
||||
for range_, color in COLOR_RANGES:
|
||||
if xtotal >= range_:
|
||||
return COLORS[color]
|
||||
|
||||
|
||||
def get_badge(total, color=DEFAULT_COLOR):
|
||||
"""
|
||||
Read the SVG template from the package, update total, return SVG as a
|
||||
string.
|
||||
"""
|
||||
template_path = os.path.join('templates', 'flat.svg')
|
||||
template = pkg_resources.resource_string(__name__, template_path).decode('utf8')
|
||||
return template.replace('{{ total }}', total).replace('{{ color }}', color)
|
||||
|
||||
|
||||
def parse_args(argv=None):
|
||||
"""
|
||||
Parse the command line arguments.
|
||||
"""
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('-o', dest='filepath',
|
||||
help='Save the file to the specified path.')
|
||||
parser.add_argument('-p', dest='plain_color', action='store_true',
|
||||
help='Plain color mode. Standard green badge.')
|
||||
parser.add_argument('-f', dest='force', action='store_true',
|
||||
help='Force overwrite image, use with -o key.')
|
||||
parser.add_argument('-q', dest='quiet', action='store_true',
|
||||
help='Don\'t output any non-error messages.')
|
||||
parser.add_argument('-v', dest='print_version', action='store_true',
|
||||
help='Show version.')
|
||||
parser.add_argument('-i', dest='reportFilename',
|
||||
help='Jacoco report')
|
||||
|
||||
# If arguments have been passed in, use them.
|
||||
if argv:
|
||||
return parser.parse_args(argv)
|
||||
|
||||
# Otherwise, just use sys.argv directly.
|
||||
else:
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def save_badge(badge, filepath, force=False):
|
||||
"""
|
||||
Save badge to the specified path.
|
||||
"""
|
||||
# Validate path (part 1)
|
||||
if filepath.endswith('/'):
|
||||
print('Error: Filepath may not be a directory.')
|
||||
sys.exit(1)
|
||||
|
||||
# Get absolute filepath
|
||||
path = os.path.abspath(filepath)
|
||||
if not path.lower().endswith('.svg'):
|
||||
path += '.svg'
|
||||
|
||||
# Validate path (part 2)
|
||||
if not force and os.path.exists(path):
|
||||
print('Error: "{}" already exists.'.format(path))
|
||||
sys.exit(1)
|
||||
|
||||
# Write file
|
||||
with open(path, 'w') as f:
|
||||
f.write(badge)
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
"""
|
||||
Console scripts entry point.
|
||||
"""
|
||||
args = parse_args(argv)
|
||||
|
||||
# Print version
|
||||
if args.print_version:
|
||||
print('coverage-badge v{}'.format(__version__))
|
||||
sys.exit(0)
|
||||
|
||||
total = get_total(args.reportFilename)
|
||||
color = DEFAULT_COLOR if args.plain_color else get_color(total)
|
||||
badge = get_badge(total, color)
|
||||
|
||||
# Show or save output
|
||||
if args.filepath:
|
||||
path = save_badge(badge, args.filepath, args.force)
|
||||
if not args.quiet:
|
||||
print('Saved badge to {}'.format(path))
|
||||
else:
|
||||
print(badge, end='')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="a">
|
||||
<rect width="99" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#a)">
|
||||
<path fill="#555" d="M0 0h63v20H0z"/>
|
||||
<path fill="{{ color }}" d="M63 0h36v20H63z"/>
|
||||
<path fill="url(#b)" d="M0 0h99v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
|
||||
<text x="31.5" y="14">coverage</text>
|
||||
<text x="80" y="15" fill="#010101" fill-opacity=".3">{{ total }}%</text>
|
||||
<text x="80" y="14">{{ total }}%</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 926 B |
@@ -1,137 +0,0 @@
|
||||
plugins {
|
||||
id 'idea'
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-kapt'
|
||||
id 'com.github.triplet.play' version '2.6.2'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion COMPILE_SDK_VERSION as Integer
|
||||
|
||||
def secretPropsFile = file("../../.secret/gradle.properties")
|
||||
if (secretPropsFile.exists()) {
|
||||
def secrets = new Properties()
|
||||
secretPropsFile.withInputStream { secrets.load(it) }
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile file(secrets.LOOP_KEY_STORE)
|
||||
storePassword secrets.LOOP_STORE_PASSWORD
|
||||
keyAlias secrets.LOOP_KEY_ALIAS
|
||||
keyPassword secrets.LOOP_KEY_PASSWORD
|
||||
}
|
||||
}
|
||||
buildTypes.release.signingConfig signingConfigs.release
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionCode VERSION_CODE as Integer
|
||||
versionName "$VERSION_NAME"
|
||||
minSdkVersion MIN_SDK_VERSION as Integer
|
||||
targetSdkVersion TARGET_SDK_VERSION as Integer
|
||||
|
||||
applicationId "org.isoron.uhabits"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
|
||||
debug {
|
||||
testCoverageEnabled true
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
abortOnError false
|
||||
disable 'GoogleAppIndexingWarning'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.all {
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed", "standardOut", "standardError"
|
||||
outputs.upToDateWhen { false }
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.assets.srcDirs += '../uhabits-core/src/main/resources/'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(":uhabits-core")
|
||||
implementation project(":android-base")
|
||||
implementation project(":android-pickers")
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.0.0'
|
||||
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation "com.github.paolorotolo:appintro:3.4.0"
|
||||
implementation "com.google.dagger:dagger:$DAGGER_VERSION"
|
||||
implementation "com.jakewharton:butterknife:8.6.1-SNAPSHOT"
|
||||
implementation "org.apmem.tools:layouts:1.10"
|
||||
implementation "com.google.code.gson:gson:2.8.5"
|
||||
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION"
|
||||
|
||||
compileOnly "javax.annotation:jsr250-api:1.0"
|
||||
compileOnly "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION"
|
||||
kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
|
||||
kapt "com.jakewharton:butterknife-compiler:10.2.1"
|
||||
annotationProcessor "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION"
|
||||
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.1.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
|
||||
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
|
||||
androidTestImplementation "com.google.dagger:dagger:$DAGGER_VERSION"
|
||||
androidTestImplementation "com.linkedin.testbutler:test-butler-library:1.3.1"
|
||||
androidTestCompileOnly "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION"
|
||||
androidTestAnnotationProcessor "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION"
|
||||
androidTestImplementation 'androidx.annotation:annotation:1.0.0'
|
||||
androidTestImplementation 'androidx.test:rules:1.1.1'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation "com.google.guava:guava:24.1-android"
|
||||
androidTestImplementation project(":uhabits-core")
|
||||
kaptAndroidTest "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
|
||||
|
||||
// mockito-android 2+ includes net.bytebuddy, which causes tests to fail.
|
||||
// Excluding the package net.bytebuddy on AndroidManifest.xml breaks some
|
||||
// AndroidJUnitRunner functionality, such as running individual methods.
|
||||
androidTestImplementation "org.mockito:mockito-core:1.10.19"
|
||||
androidTestImplementation "com.google.dexmaker:dexmaker-mockito:1.2"
|
||||
|
||||
testImplementation "com.google.dagger:dagger:$DAGGER_VERSION"
|
||||
testImplementation "org.mockito:mockito-core:2.8.9"
|
||||
testImplementation "org.mockito:mockito-inline:2.8.9"
|
||||
testImplementation "junit:junit:4.12"
|
||||
|
||||
implementation('com.opencsv:opencsv:3.10') {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
}
|
||||
implementation('io.socket:socket.io-client:0.8.3') {
|
||||
exclude group: 'org.json', module: 'json'
|
||||
}
|
||||
}
|
||||
|
||||
kapt {
|
||||
correctErrorTypes = true
|
||||
}
|
||||
|
||||
play {
|
||||
serviceAccountCredentials = file("../../.secret/gcp-key.json")
|
||||
track = "alpha"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<lint>
|
||||
</lint>
|
||||
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 605 B |
|
Before Width: | Height: | Size: 541 B |
|
Before Width: | Height: | Size: 586 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |