Compare commits
1221 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0cce6b30b1 | |||
| bf650a7565 | |||
| b78cd1dd0d | |||
| 6d9ad8c56c | |||
| e7a3f0cffa | |||
| 6aa72caf6c | |||
| a3344358b9 | |||
| 22fcecb48c | |||
| 58d8c799ce | |||
| f9437d61b0 | |||
| 997ebfc28a | |||
| 6a91300d82 | |||
| 889ce9faef | |||
| b3a40efe46 | |||
| 547c7ffdf7 | |||
|
|
82486c7514 | ||
|
|
fe732ea385 | ||
|
|
85ec0faa99 | ||
|
|
9ac1ae9915 | ||
| 895d66b663 | |||
| 0f2a93cd27 | |||
| 323c98edb8 | |||
| aadfac68cd | |||
| 7076cffec5 | |||
| 0ef5a8dead | |||
| 526c8fe750 | |||
| 464eaf613d | |||
| b21d77514a | |||
| 58ed759224 | |||
| 7c742e1016 | |||
| 2e9330b5c5 | |||
| d1b83d069d | |||
| 7d35a85a37 | |||
| 616d9ab46c | |||
| b9c9d6852a | |||
| 29e4a8d4ec | |||
|
|
ceed784acd | ||
|
|
897887f802 | ||
|
|
dcd0c61b8d | ||
| 1285b653ed | |||
| dfd5c65595 | |||
| 315bddea96 | |||
| 938739d535 | |||
| 27f625873d | |||
| f37d25e86b | |||
| b2df70c059 | |||
| 3afd46c59c | |||
| e36c649333 | |||
| b2e7c9fe6e | |||
| 2171d582dc | |||
| b7be459537 | |||
|
|
cbee09c38f | ||
| 6f24e42d1f | |||
| 93bb105dac | |||
| f67b74d57d | |||
| abbf1e4d66 | |||
| e3f6353062 | |||
| 38d5b2bf16 | |||
| 7801c933f0 | |||
| 5cdb9eb9d5 | |||
| dee4670f65 | |||
| 50a6d7190b | |||
| d3371badf8 | |||
| ed1f3e5cdb | |||
| 5947926cd5 | |||
| 61cd77e5df | |||
| d6ec33d6d3 | |||
| 3f002efb53 | |||
| 5191290188 | |||
| cc05543692 | |||
|
|
e94e0c057e | ||
| 350f002ed3 | |||
| 04cf4e7785 | |||
| bf644f4e09 | |||
| 3658aef2e2 | |||
| 190a1171c4 | |||
| 2b20b6bb9d | |||
| caed1aef79 | |||
| 8341956a90 | |||
| de44b48dba | |||
| 7eafd92b2d | |||
| 47dc26fea0 | |||
| a6dd0939b2 | |||
| 898a33a754 | |||
| 1997d9491b | |||
| 6d57ea0368 | |||
| 83c1197dc1 | |||
| 48d145626f | |||
| a57f310c76 | |||
| 1e6cb2e841 | |||
| c676a02ca8 | |||
| ded57cd04a | |||
| b8f9e2f309 | |||
|
|
2fb9168686 | ||
| 9199a64d73 | |||
|
|
a69fb369df | ||
| 0790961bb5 | |||
| c88fa4a003 | |||
| f403dfd7d1 | |||
| 105baf629a | |||
|
|
af37036ac5 | ||
|
|
8717ad6ad0 | ||
|
|
95af0217ff | ||
|
|
fe091fa740 | ||
|
|
00abb4486d | ||
|
|
dadfcb7c16 | ||
|
|
77a9701805 | ||
| bfea4b024a | |||
|
|
3e2cf48223 | ||
| ec2fa16fab | |||
| 8d97a8d140 | |||
| 563aa8b7b4 | |||
| 74475bd191 | |||
| 957a5b7c17 | |||
| 64cc9f78a8 | |||
| e50c411d1e | |||
| 046a7eab7f | |||
| d96732b588 | |||
| a3bfc05068 | |||
| defa2f9431 | |||
| fe4139e268 | |||
| d463bb55d7 | |||
| 5ea19c9475 | |||
| b0cedde0a9 | |||
| e0894c9313 | |||
| 8378d88186 | |||
| ddd363917c | |||
| f310eaf7d9 | |||
| 8972f2d03d | |||
| e88c58916a | |||
| 7d169d8053 | |||
| c018d89ca6 | |||
| 5c402b5400 | |||
| 7ba7edb7d4 | |||
| fe219b5296 | |||
| 1abc041d87 | |||
| c16a0ecd65 | |||
| 6d527a31d7 | |||
| 4d0d631d8a | |||
| 5b2b554a7a | |||
| 33bae657a3 | |||
| 082c575f82 | |||
| 90f553b4f6 | |||
| 6af576c09c | |||
| c380abad5a | |||
| 905099ccdb | |||
| 76b848752c | |||
| 48c3ff584a | |||
| 979affef22 | |||
| 274d3d6858 | |||
| f491acdda9 | |||
| 6ac7ef7807 | |||
| 5d1f5168ad | |||
| 262b9460bd | |||
| f4e4da6dc5 | |||
| 024c99e60d | |||
| aa6b13f3a6 | |||
| 70a79856f2 | |||
| 6a30bb98c6 | |||
| 07cf74d400 | |||
| 94b35545b7 | |||
| 8544c5dc8a | |||
| a546f6de73 | |||
| 7cab0a39e5 | |||
| e19339d808 | |||
| 134975b8ba | |||
| 2d0a57efbd | |||
| 50e94c4d09 | |||
| 15974bdca8 | |||
| 0734e74154 | |||
| f108bc8dc1 | |||
| 4ccda9d6f7 | |||
| db1ba822fe | |||
| bd18a4320a | |||
|
|
b180f11834 | ||
|
|
d4b5c7b9d5 | ||
| a719f6ad98 | |||
|
|
d8894753e0 | ||
| 178061475e | |||
| 3581173193 | |||
| a839631aae | |||
| a3e0d7ffb1 | |||
| 6d4dbcdee7 | |||
| 33ae289ff1 | |||
| fca695ee6b | |||
| 8fd175685d | |||
| 6a3e430a5e | |||
|
|
07efa8b321 | ||
| 9183cb9f37 | |||
| a1bd4836dd | |||
| dc74c0e54b | |||
| 436d19dfea | |||
| 6ca4877f1f | |||
| 6ad302b697 | |||
| b0820095f1 | |||
| b5fda334d4 | |||
| 88beb7b883 | |||
| 49689317b7 | |||
| 052d26c708 | |||
| 76c88848b2 | |||
| 87f069f986 | |||
| ce27773138 | |||
| 0864f83307 | |||
| 624cc67d9b | |||
| 462bac8167 | |||
| 5865eb41f7 | |||
| 2bfd4a942d | |||
| b4a33cba39 | |||
| f02c86e61b | |||
| 5021f50e18 | |||
| 2654521647 | |||
| 778a7eb6bc | |||
| 71d559d6d9 | |||
| b94d2f2fa6 | |||
| 2904f3e2f8 | |||
| 1ad06bcc15 | |||
| bf8c14fc03 | |||
| b46b7aae25 | |||
| 357f51fd5e | |||
| 7f257e045b | |||
| 0dc46d02a4 | |||
| ecf3086aef | |||
|
|
10be875b48 | ||
| 0077d35ff9 | |||
| 2a4a7c975f | |||
| e91f1c3fa4 | |||
| 9d48b4bcdb | |||
| 7f1a35ebe5 | |||
| 5ccd546958 | |||
|
|
def71d8141 | ||
|
|
8feb07ff1b | ||
|
|
1edd76ae8c | ||
| 7613e6e1cb | |||
| 5629a28823 | |||
|
|
b1c2ab90d3 | ||
|
|
268cb0bc18 | ||
|
|
5a78de5a25 | ||
|
|
82972d6e47 | ||
| a201273781 | |||
| 819a8d341f | |||
| b8f7d4fad2 | |||
| dea4e069c5 | |||
| 07c7234bfc | |||
| bf3964a231 | |||
| 657cde75d8 | |||
| c56b86d32c | |||
| 16f20d50a0 | |||
| 7bb88dcb97 | |||
| 223b8bc5ec | |||
|
|
e052a144bd | ||
|
|
318caa886c | ||
|
|
46c61f9ea9 | ||
| ba78e563cd | |||
| acb94db6d6 | |||
|
|
d2cc283bd5 | ||
| 6c66078a65 | |||
|
|
be9c2ff64d | ||
| bb22972eb2 | |||
| e911fb35b6 | |||
| d1490ee771 | |||
| 6d44b4124d | |||
|
|
f8e0d07236 | ||
|
|
e970473876 | ||
|
|
cec05ccbca | ||
|
|
aac59367dc | ||
|
|
0421ca0549 | ||
|
|
43e802fb8e | ||
| 52c4282601 | |||
| 665204bf7a | |||
|
|
f52da56221 | ||
|
|
4d59783809 | ||
|
|
a680d57cac | ||
|
|
f6620be2d9 | ||
|
|
1c2abb543b | ||
|
|
87cf2871a7 | ||
|
|
e041d9041b | ||
| dccf5eae47 | |||
| b2a8c9c45f | |||
| 125a574ff9 | |||
| 7bb62c197f | |||
| a75a27ad42 | |||
| 4126f01ef1 | |||
| 9816fc9127 | |||
| b135aa09a3 | |||
| f3a64fd67a | |||
| 864636705d | |||
| 281861cac5 | |||
| c05f50998f | |||
| b2734b179c | |||
| 00a4abf266 | |||
| 47e279b3b3 | |||
| 87f1d635d8 | |||
| fdcb9daadc | |||
| 5fde0501b5 | |||
| f72799f48c | |||
| 60c62b8609 | |||
| e5bc06c138 | |||
| fa5ba0c1ef | |||
| 43be70b27c | |||
| 89400e281e | |||
| 57dc19550d | |||
| a8aa6f192c | |||
| 882ddba324 | |||
| 2f7509b94e | |||
| 10e68aa008 | |||
| a5720e8d7f | |||
| c7aaa98935 | |||
| efcb5710c0 | |||
| 3783fd8506 | |||
| 6f80a9c030 | |||
| a02376497a | |||
| 180c18f6bf | |||
| 2db4c06fe8 | |||
| 540a618ba8 | |||
| bf24cc608c | |||
| a73459784e | |||
| de3b97dfdf | |||
| 91996924d9 | |||
| 9fe446b424 | |||
| 3857eaf5e9 | |||
| e29fb58922 | |||
| 404fc869b0 | |||
| 001dd5a7c1 | |||
| 7930cc8f31 | |||
| 122b300c50 | |||
| 0984f7ff5d | |||
| 2d9a5ae7e2 | |||
|
|
02f9660fda | ||
|
|
d3d733ab42 | ||
| bb282da92d | |||
| 38d3b0d047 | |||
| 8ccada67d6 | |||
| 33f7acc9ca | |||
| 5d9563b9d8 | |||
| f55dc0d811 | |||
| 7872983064 | |||
| ea640a8a17 | |||
| 6801d1d1ae | |||
| 3584affbe0 | |||
| 1069fcfc62 | |||
| 59745fb90f | |||
| 1976160ae8 | |||
| 6dd7e49112 | |||
| ecb5352134 | |||
| b96385c4a7 | |||
| 96c1a046d4 | |||
| 00660d3e36 | |||
|
|
b14ca5c625 | ||
| 71fe6137be | |||
| 0a5d565030 | |||
| 6d06e06840 | |||
| edeba897fb | |||
| e4b9c50ee2 | |||
| af7c4e227d | |||
| 96ab887545 | |||
| 526830ba61 | |||
| fe1513bb64 | |||
| e06ace9ea8 | |||
| d727dabb2b | |||
| d17e8fcbfb | |||
| f8a9da59dd | |||
| 50a6c6d9dd | |||
| 8d181a6683 | |||
| 217516ad59 | |||
| fc4b610d59 | |||
| 382b52e5b2 | |||
| be3d7145ab | |||
| cf66587644 | |||
| 0dc9ec2e5f | |||
| 0a375ded96 | |||
| fa5d6f8fee | |||
| 277738f94d | |||
| cf25229fbc | |||
| 7652d71c94 | |||
| 74e0dcf706 | |||
| 4e1cc6dc80 | |||
| c34f9f9e9f | |||
| ed9066f393 | |||
| 96e1771c25 | |||
|
|
d0d3c7eef5 | ||
| 6875fc0428 | |||
| b88b3a683d | |||
| d97f94075d | |||
| 28f095e56a | |||
| ead87519b1 | |||
| 7cfe3355e4 | |||
| a51ecaaf24 | |||
| 322645da9b | |||
| 5eb63df633 | |||
| e4b5a3ea45 | |||
| 88c1e73720 | |||
| 0393e58d3b | |||
| 534e6c2d9d | |||
| b6501c9a29 | |||
| 238a1c724d | |||
| 34ca9d17a2 | |||
| 6ccfb53329 | |||
| 8a29fbf07d | |||
| e844390614 | |||
| 223aee3be2 | |||
| 745d07024c | |||
| 94025c5262 | |||
| ab09eb8a03 | |||
| e826c80ff2 | |||
| 6255fe2d12 | |||
| 1746920699 | |||
| c9b62669de | |||
| d2f367678f | |||
| 5e00d07b73 | |||
| 28b6ae7014 | |||
| 2a1bf5fc2e | |||
| ef7483f9dc | |||
| bbb9ed8f99 | |||
| c49d576871 | |||
| bc66ae4f7a | |||
| 56c5fb6c9d | |||
| d8d4c4f55e | |||
| 70423ddb0a | |||
| 94c70485b7 | |||
| 3e558be4d4 | |||
| 95385fa8f4 | |||
| fa4944700c | |||
| df0cf57984 | |||
| 29d1de46e7 | |||
| cb4ab3b436 | |||
| 370e7343d7 | |||
| acd653db70 | |||
| 51ca4aa98e | |||
| d23b59ced2 | |||
| f18ac9db48 | |||
| c20ca3921f | |||
| 96f620455f | |||
| 2dd14dbf04 | |||
| 8346f28497 | |||
| 97967b2b2e | |||
| cf6a257143 | |||
| 275125d230 | |||
| cb7b569d4e | |||
| 89b6f4c5cc | |||
| 8d555eb837 | |||
| 9288528f94 | |||
| e92ffb3894 | |||
| 96bdb42365 | |||
| ecb207d322 | |||
| 64001604cf | |||
| b9d3d22894 | |||
| 25705297cb | |||
| 532637ef7e | |||
| 0a1907ee2c | |||
| fa416adbb9 | |||
| 8b835b9918 | |||
| 471c5d341f | |||
| d0e76d3d55 | |||
| 1832ea639b | |||
| cd3944b90f | |||
| 9f2f8f7117 | |||
| 1b97b9040d | |||
|
|
8a80a66a80 | ||
|
|
894423e49f | ||
|
|
4ce9013e6a | ||
| 96b95edef8 | |||
| 704854fdf1 | |||
| 6e0393f611 | |||
| e6ceed9ec9 | |||
| cf3d289145 | |||
| 6bd59aad97 | |||
| a5567d491f | |||
| 9604c26973 | |||
| 81ad1ba8c8 | |||
| 1d68122a6f | |||
| 7d9d45ffed | |||
| 50e21b2cc1 | |||
| 57296745b3 | |||
| fb1b1221d3 | |||
| 788c790f9e | |||
| 293d838d80 | |||
| 3da996b8a4 | |||
| 08e3c9cc40 | |||
| 13b4128777 | |||
| b4e79c3f4b | |||
| 2d07f5e924 | |||
|
|
cb29fef17e | ||
| 1d76760dc8 | |||
| b6994034d2 | |||
| 8fc0c072e5 | |||
| e6deb1f281 | |||
|
|
bb0d43018e | ||
| 140ab34a76 | |||
| 0d6ad26505 | |||
| 65cc99dbf7 | |||
| 6855ef9d5e | |||
| 4c58b084c6 | |||
| 5c8e522646 | |||
| 55da0759d4 | |||
|
|
814b734ad3 | ||
| 6f5941472b | |||
| 7699423aa7 | |||
| 692fe3218f | |||
| 387930c08d | |||
| 6bd31f9607 | |||
| 9aafe7160c | |||
| 5cc4aac67a | |||
| 831421bc98 | |||
| e48dab9ed3 | |||
| 161d8f2517 | |||
| bfe4b822b3 | |||
| 53a599c6b8 | |||
| 103c0b57f8 | |||
| 7de69c1c10 | |||
| fe7e8ef039 | |||
| 19f4a19dba | |||
| b66da24e39 | |||
| 3dd33274e4 | |||
| c61834e604 | |||
| e082705b0c | |||
| 83ce92d8ac | |||
| 177525817c | |||
| 1dfa0c6b0d | |||
| b3f039d658 | |||
| 7d2e8573f8 | |||
| 5653651c0d | |||
| d3b540199c | |||
| f0430ffeb3 | |||
| d03edf2895 | |||
| 5c1ccfe6fe | |||
| 5b9e90fe7a | |||
| ac32460859 | |||
| e2a8de3acf | |||
| 7bf9f88ee3 | |||
| 19e79a8559 | |||
| 876d4f0ac7 | |||
| f4f7faf3a4 | |||
| 56f2ae57fe | |||
| 3fe09efe9b | |||
| f0de29fbfe | |||
| 324facfffd | |||
| 03e58f9ef2 | |||
| e6c9f7f0c9 | |||
| 42bdedb86a | |||
| ab0c510fda | |||
| e46fd58664 | |||
| 8532bd402e | |||
| 2c599b18ef | |||
| 0d78ba4ba9 | |||
|
|
611dfa00a5 | ||
|
|
54a195243d | ||
|
|
4fc30fae53 | ||
| b3fe9c65d2 | |||
| 09f1ae8765 | |||
| 0a8b763ece | |||
| edd5f25529 | |||
| d81fdb41dc | |||
| 02c8810e46 | |||
| 6adf8061d3 | |||
| d19d57e5df | |||
| c20d5c8729 | |||
| fd82e6c24b | |||
| 56263efa39 | |||
| d5eacba303 | |||
| 222261c674 | |||
| b1a06df7f8 | |||
| a1fc7dd0d1 | |||
|
|
10131d5124 | ||
| aa94959ad2 | |||
| 967dc2586b | |||
| 908fd1d6ae | |||
| 45fd8a29e1 | |||
| 4624acd477 | |||
| c8cd4fa389 | |||
| 8c4fab28aa | |||
| d6b91cef01 | |||
| e273fe7375 | |||
| ab3b946c65 | |||
| 38cd47c199 | |||
| fa64bd389d | |||
| d03578fd9e | |||
| 79ae181df2 | |||
| 320dca9070 | |||
| f3b9fc825a | |||
| 50da14022f | |||
| 84a02fe541 | |||
| b3aa3d14c0 | |||
| e9cdfd23c4 | |||
| 659ad2d817 | |||
| d537ba0dfa | |||
| 7c14725d88 | |||
| 638b3f763c | |||
| f876fc50bb | |||
| 919504ccfb | |||
| 1a8d9e72a1 | |||
| 8e82f369c7 | |||
| 0a5677211e | |||
| 65071797c9 | |||
| df1751b21a | |||
| 4e952dd87a | |||
|
|
132dce8919 | ||
| 5c8450191a | |||
| b1d00598eb | |||
| 518ade3165 | |||
| dc5d7930a6 | |||
|
|
ec34043041 | ||
| 4d5407a5cc | |||
| 904489d812 | |||
| d2436a3165 | |||
|
|
bc19858bca | ||
| a998a62cdb | |||
| 1a89bb02be | |||
| 6e8ea471aa | |||
| ec42fb54f4 | |||
| c46fa84135 | |||
| 16dcc0cbc2 | |||
| 6f10039aba | |||
|
|
5cbc0a3292 | ||
| 7f67a9eb63 | |||
| d3f7ebd60c | |||
| 461fe1f0b6 | |||
| 8eb9f398d5 | |||
| f178bcbdd2 | |||
| 66c3136fad | |||
| 4b04966617 | |||
| 3ea21fe823 | |||
| e0df69beb6 | |||
| b5cd4584b2 | |||
| af7c8b1f2e | |||
|
|
283cb2a3f0 | ||
| 79ddbca307 | |||
| 46a0777195 | |||
| b35305e16c | |||
| 8c4a745ecb | |||
|
|
e5162c48ab | ||
| b54c4de5f7 | |||
| 2bdc0b4f5e | |||
| 22db61db01 | |||
| c7cfbd1643 | |||
| 8655437f3e | |||
| 3ad3cf54ec | |||
| 9a470cc61d | |||
| fc2087fe68 | |||
| 7b8ab6a625 | |||
| 2d40fb0b82 | |||
| c961045b63 | |||
| d0db3359fe | |||
| 11378e07bf | |||
| 8132188e46 | |||
| 7eb454788f | |||
| c262adbe85 | |||
| 05aa5b1172 | |||
| dd3d78b82c | |||
| 3d4ae2126b | |||
| 5aa9114aff | |||
| 77e0ad007f | |||
| 2e53b75705 | |||
|
|
0ec03035f5 | ||
|
|
67ae48b527 | ||
|
|
82d2931559 | ||
| a527140802 | |||
| 9ec3d9048a | |||
| 3ed4f3b280 | |||
| f101975320 | |||
| 3a7f27755c | |||
| 3a3be664f7 | |||
| 33ae8d1edf | |||
| cb3b8f2cde | |||
| 307e025b1a | |||
| 03dd24d17b | |||
| 5f4ac21a41 | |||
| b33616d363 | |||
| b7d1c6d254 | |||
| bd30ceee1b | |||
| fe96313162 | |||
| 938fe3325e | |||
| 237de035bb | |||
| ddc85ced0b | |||
| eceb1bfb7d | |||
| 3b737996e9 | |||
| 7f75f9b6da | |||
| 74f78f0fdf | |||
| ef63dd19e7 | |||
| c7878d979f | |||
| ebd294be63 | |||
| 748cec06a8 | |||
| 15a4a2c002 | |||
| 37a9e793e7 | |||
| d54de9df89 | |||
| 94a5db2208 | |||
| a984467516 | |||
| d6dacfd24b | |||
| 3938ae6fa8 | |||
| 7dbbc51a9a | |||
| c9d1bb821c | |||
| 77f406dcee | |||
| 61b0b1fdea | |||
| 8cde0d6aca | |||
| fa9f90a09e | |||
| 28eb615b0e | |||
| c1e10e09a5 | |||
| 35e93fddc6 | |||
| 33596a2797 | |||
| 207f026ceb | |||
| 0f12d02990 | |||
| bc4bbaefac | |||
| a802053ef7 | |||
| cf98d1a5c3 | |||
| cc1e56894b | |||
| 06b5f89b7a | |||
| 17423b3ecd | |||
| 1526f617c5 | |||
| 365eb400d0 | |||
| 785d57c778 | |||
| 8e9f1aa166 | |||
| b2f97cb0a5 | |||
|
|
4c1ff4f0a8 | ||
| 94a48133ec | |||
| 71dd6cde89 | |||
| b33420cabb | |||
| dbe268b8e9 | |||
| 3d53812d7f | |||
| 5b4b436f0f | |||
| 6ef2983906 | |||
| b749f7f391 | |||
| 922b234307 | |||
| 31fdae1c8b | |||
| 5d61fdd3d0 | |||
| 588b45d47b | |||
| 9a059275ce | |||
| 4ae813e6f9 | |||
| 52701666bc | |||
| a90e26691f | |||
| f0c62a5908 | |||
| e899a70eb0 | |||
| aa41717c66 | |||
| 6ba6d7c8c1 | |||
| efd0d1e051 | |||
| a11ad6e909 | |||
| a060cbe578 | |||
| a445ca962b | |||
| 2b6fc06b86 | |||
| 98ad3aab9d | |||
| c7e63a40da | |||
| b6ed33b1e6 | |||
| 91e39372a1 | |||
| c2dd26eeb3 | |||
| 5831340343 | |||
| d7f6f52a49 | |||
| 93b442332d | |||
| e248824bcd | |||
| ec4a381d70 | |||
| b13f2b4228 | |||
| efc7b2cebb | |||
| add08d6054 | |||
| 5d8a348aaf | |||
| ec0e8ac24c | |||
| fee3137a6f | |||
| abe6b10964 | |||
| 6484b96e5a | |||
| 3d3d5b9b96 | |||
| 14364901ff | |||
| 440706882b | |||
| ca9d56e59e | |||
| 9a6dafaa79 | |||
| 9a44774284 | |||
| 2b23b36e36 | |||
| 78d4f86cab | |||
| 18e8390aed | |||
| cabcd5b1bf | |||
| 7e8a2a0c1c | |||
| 650971bf36 | |||
| 00774368d4 | |||
| cbf1bd3e19 | |||
| 4061921b93 | |||
| 59d42fe62f | |||
| 3ffa079e24 | |||
| bb950d61fc | |||
| 0ad0e5cf36 | |||
| 3b56d6d596 | |||
| 8d57273987 | |||
| b98853ab26 | |||
| 071cad73d4 | |||
| 16dc9c25d2 | |||
| 6b23858136 | |||
| e0198e9926 | |||
| 6445bf62bc | |||
| 83ef8564e1 | |||
| 88e8aad0d8 | |||
| a4b6728721 | |||
| e6e80b9841 | |||
| 442c3ed78d | |||
| ca9745f550 | |||
| 33bc8f78e7 | |||
| 0f223f8504 | |||
| 007996c69e | |||
| ff9d50b32a | |||
| 38024d71ce | |||
| 9ec1afc208 | |||
| e0888a9b4d | |||
| 8aade2f145 | |||
| 1ba1d775f7 | |||
| 605593d739 | |||
| bfbfcbd8cf | |||
| bd6cba8303 | |||
| befc3a0ad8 | |||
| f4c963e2c1 | |||
| e9a4a047c1 | |||
| 7ce1988d2e | |||
| 53911fa410 | |||
| f5f43d9a16 | |||
| 2399dccddc | |||
| dd5f37290c | |||
| dbf569ff87 | |||
| d0e475ad78 | |||
| b3199cf092 | |||
| 73e6f2a2d4 | |||
| 5b402478e9 | |||
| e3b7e9f60f | |||
| b0040bd83c | |||
| 56e1268f85 | |||
| 41d9e2f0f5 | |||
| 1fcfb9b22e | |||
| 2c80544aaa | |||
| 097a5be864 | |||
| 0287473e61 | |||
| f403dc6f77 | |||
| 23466523df | |||
| 83ca136346 | |||
| 7830b599db | |||
| 49376d4f41 | |||
| 8ee9c9c0b1 | |||
| ee9da29422 | |||
| ae7b0408b2 | |||
| d926d9dbd6 | |||
| 5694da2413 | |||
| 7e0ae144b8 | |||
| 1426d887e7 | |||
| acb8e820fd | |||
| eae0d66f51 | |||
| e933cbbc43 | |||
| 0b95b6a78c | |||
| 5e873a3659 | |||
| d292ecd988 | |||
| 19b484c368 | |||
| 834ae92d87 | |||
| 26ce92d381 | |||
| 0eb12812d4 | |||
| a1a636c718 | |||
| b2811b9797 | |||
| 4db7a6e89c | |||
| 677a643e5b | |||
| dbca3238f6 | |||
| ae5dd700f3 | |||
| 7e4c508d7d | |||
| d0c056eeaa | |||
| f172c69eed | |||
| 0cd4a41438 | |||
| d77c78249c | |||
| d9d48ff984 | |||
| 7be71ba4f8 | |||
| f0534fefbc | |||
| 30ef75bb45 | |||
| 44ed74a693 | |||
| 4aea527e07 | |||
| 638539259a | |||
| 75dec88411 | |||
| 2dc4fcbc46 | |||
| 7977d5247c | |||
| 8b18e32a16 | |||
| dbcad9a5f4 | |||
| 3bba75ff50 | |||
| 9bbeee66e2 | |||
| c6e76f4cfd | |||
| 980db1d171 | |||
| aee5d975db | |||
| e2bb4371d3 | |||
| fcee8552f0 | |||
| a4864e4612 | |||
| 8ddf4574e3 | |||
| f1ca8c5449 | |||
| 81a188f125 | |||
| 44402bf3a4 | |||
| 7f159149ef | |||
| 0c1e8d5131 | |||
| a4e3f7e037 | |||
| b354a0765b | |||
| bbd959dfda | |||
| d05d404c55 | |||
| 8938b0c9a6 | |||
| bd6fcd066c | |||
| 6a5f2abb76 | |||
| 33b5215b00 | |||
| 5d808dd2e8 | |||
| 767ec1b6de | |||
| 9e5f3d8f58 | |||
| 775d028629 | |||
| 4bfb839370 | |||
| 8f64b696d8 | |||
| c4bf31e778 | |||
| c757ce6548 | |||
| 78994bb3c4 | |||
| 2373ba5407 | |||
| b601a643dd | |||
| 90e513e778 | |||
| 728c9557f0 | |||
| 5bd0c842f5 | |||
| 97711087f9 | |||
| 2fe2d8834a | |||
| 93a893d660 | |||
| feff9a18e6 | |||
| b42565b770 | |||
| 3de702ced2 | |||
| 42f7f4042d | |||
| 2115a590f2 | |||
| f5f3ec9f88 | |||
| 6d5a8f5753 | |||
| 06fc04092b | |||
|
|
24c02605d1 | ||
| 11fcb67624 | |||
| abc92e390a | |||
| 52c07660b1 | |||
| 35f778c376 | |||
| 436ae7e574 | |||
| 5c683a77c2 | |||
| dd1f6c9efc | |||
| cba089407b | |||
| a7b0395a2a | |||
| 7d2946360f | |||
| 04e8432522 | |||
| 13d34945b4 | |||
| 756578508e | |||
| 38fc7650b9 | |||
| cf06ec0a4b | |||
| f0701f7b35 | |||
| de8018af7d | |||
| ac885e1503 | |||
| f85e09288c | |||
| 6f11596bb7 | |||
| 64c4367706 | |||
| c08702341a | |||
| abb8c09bc9 | |||
| 87255ceb25 | |||
| aedbfded0f | |||
| 779c0040bd | |||
| 7bba6a887f | |||
| 52a1d19793 | |||
| 35e346e7ea | |||
| 3102158bdb | |||
| 7afa65a53c | |||
| 2e5533d480 | |||
| b29c7e695a | |||
| 7df745eb82 | |||
| c0d518ca4d | |||
| eeff14a2ca | |||
| af3b18f518 | |||
| 3811f4b339 | |||
| 50a8aece6f | |||
| 2f66cfc417 | |||
| 132ce36a57 | |||
| 147f010d1b | |||
| afc91ed89a | |||
| 684c37d167 | |||
| 8b296ce2ae | |||
| e2e9e2e27a | |||
| 8b2f31c44a | |||
| 83db9fe2f2 | |||
| 9891139b5a | |||
| cbc8cbfea3 | |||
| 96c46b655d | |||
| 0de52d4fa3 | |||
| 16d85dc4c7 | |||
| c51b1fd399 | |||
| eb8dd1d450 | |||
| 357646152f | |||
| ae5be202b2 | |||
| cc3e2153d0 | |||
| 7433a2413d | |||
| 1cd8eb6849 | |||
| 7f009e2365 | |||
| 4cec7d7367 | |||
| f59c0bd7ff | |||
| 2d2bb8b4ed | |||
| e476096ae1 | |||
| e2c814a982 | |||
| 4fcaa68baf | |||
| 02e45dbe27 | |||
| bf6562f854 | |||
| 3c927e009a | |||
| a9bcae0f2f | |||
| bc54f3f916 | |||
| 5763d76167 | |||
| 86b66b1388 | |||
| 5e0422848b | |||
| a67a3297b1 | |||
| b2f2d4ad28 | |||
| 4dc8201dcb | |||
| f24e300b40 | |||
| 21157eaa19 | |||
| 0ea2c8e5c8 | |||
| 826491bc90 | |||
| 72f5ca5531 | |||
| f9da90a93a | |||
| 96c6a97ad0 | |||
| 20469789ea | |||
| abafffcff1 | |||
| d45a4445cc | |||
| 94f56a8869 | |||
| bc331d0a4d | |||
| 2ba7246e5f | |||
| 67b88c5012 | |||
| e0b637e84d | |||
| e14d73cf32 | |||
| 16b9584868 | |||
| c091116105 | |||
| 37e1a6233c | |||
| 6838a0f2d3 | |||
| 53e737c169 | |||
| 285f79ed95 | |||
| 4f65c8bef1 | |||
| 51b7cfc8fa | |||
| 55ed2585ea | |||
| 2c62e1578a | |||
| e3d96c0431 | |||
| b4c2d2237a | |||
| d14fbbd130 | |||
| e2c99d745e | |||
| dfe41176bc | |||
| 1ef4f8d456 | |||
| 6c810ee7a3 | |||
| 5115379fdd | |||
| d176ea91fb | |||
| 62df660079 | |||
| 790beb185a | |||
| 59c0af17d7 | |||
| 28dad560a6 | |||
| 9e410f8395 | |||
| 3656c51e95 | |||
| d3ebb4ff25 | |||
| 3c595bc79d | |||
| 1120f56dd4 | |||
| 2dfbcfcdb0 | |||
| 90a3964f0f | |||
| 6c05366f0f | |||
| dcc771abe7 | |||
| 743431ef67 | |||
| d5fc1a6886 | |||
| eeb0b109ae | |||
| ad391fa791 | |||
| e8bbae8ef9 | |||
| c9793df7c7 | |||
| 3c6114fc87 | |||
| 4ea4201b6a | |||
|
|
30b5b90091 | ||
| e6b7b8b590 | |||
| 2d675ed9b0 | |||
| 1db2f69f05 | |||
| 49a80faca3 | |||
| aa5df56437 | |||
| 581197be03 | |||
| dfe5c4954e | |||
| a0582b65d5 | |||
| 9edc3e12c9 | |||
| 7bb3db213f | |||
| e05a69b527 | |||
| d5df6ddcdb | |||
| 3016263750 | |||
| f9377e1768 | |||
| ae0dad9120 | |||
| be114bde7f | |||
| e7148abc2e | |||
| 2d87076a48 | |||
| 7da4ddf91b | |||
| e4e8d77acc | |||
| 7945a5faf9 | |||
| 3f7d25461d | |||
| 519737a1c3 | |||
| c3ff1fbe03 | |||
| d39e1978a2 | |||
| 536c095f23 | |||
| 8aaa5aca28 | |||
| 5540a66e08 | |||
| 01ffc6c144 | |||
| 21aa658acc | |||
| e17667d85d | |||
| f2948dac6f | |||
| 31a7d92f77 | |||
| 82d7970f86 | |||
| af1a21a959 | |||
| e7c1575083 | |||
| d5a14ca55b | |||
| c4d6b80944 | |||
| c4b6cad6bb | |||
| 7070530d0e | |||
| 1332a70eb4 | |||
| 11c9a3dab0 | |||
| 74f7b0fd28 | |||
| e693504183 | |||
| 7d9a94ae9e | |||
| 326cb8f73f | |||
| 6826bd1ddc | |||
| b5bc347624 | |||
| 55c058ff42 | |||
| e829bb8109 | |||
| 0921f9346e | |||
| eb017bf99b | |||
| 79b6ef8200 | |||
| 7ba62d6784 | |||
| f5e4a88415 | |||
| 075b7812eb | |||
| 3d42505fb9 | |||
| 824f98dd96 | |||
| fcb82bcb72 | |||
| 851cae3662 | |||
| 7778c5fb21 | |||
| ef847dac17 | |||
| 8102c18c67 | |||
| 59ed9ec9bd | |||
| ffdc923268 | |||
| 9232378d04 | |||
| b20fd44cbc | |||
| ded8800017 | |||
| 606f66b8d9 | |||
| 1bb6cd405b | |||
| babf7d64f0 | |||
| dfbcf78dd7 | |||
| e01c668e4d | |||
| 2eef696027 | |||
| ddd10cacd1 | |||
| 0cdde4901e | |||
| f13e9b7362 | |||
| 558f72d7c5 | |||
| 6e493f55bc | |||
| 5186ab840a | |||
| 65fd82d888 | |||
| 866b62987c | |||
| 18abb2038f | |||
| f7f4b5eeb0 | |||
| 45a7433773 | |||
| 40420c3a77 | |||
| 988b39f2e5 | |||
| a0803966f9 | |||
| e4af662836 | |||
| e95adab422 | |||
| 012fed01eb | |||
| 4cf2b8072b | |||
| 196a79a88c | |||
| 4b7e2e79a7 | |||
| 9156bba267 | |||
| 1a18bb939d | |||
| 144524e53b | |||
| 3d1c53396c | |||
| 1930db3cd1 | |||
| a2c2a5531a | |||
| eee2605f74 | |||
|
|
9df0c9ae9e | ||
| e0533505c1 | |||
|
|
433894336c | ||
| 9061182301 | |||
| 8c45f52d56 | |||
| 53c9dca3e2 | |||
| 8b37daa9fb | |||
| f9c2e83c79 | |||
| 71bdc70c1a | |||
| 5e4a40579a | |||
| d326be1224 | |||
| fb8a09c95c | |||
| 1635b9905d | |||
| 2d88fc0b20 | |||
| 1d74359c06 | |||
| f75f77cec7 | |||
| 8b10138cd6 | |||
| 2b40633110 | |||
| 1102d05a61 | |||
| 51e8c2f111 | |||
| 547e4e5f63 | |||
| 84d5c2aac6 | |||
| 2b3b423fa3 | |||
| 3b28c37c5e | |||
| e749e787ad | |||
| 9c5d582f24 | |||
| 75a4edb61c | |||
| 34c0758308 | |||
| 85963ae061 | |||
| e3390d5397 | |||
| 59a2f31a73 | |||
| c01f8450d3 | |||
| cea5241135 | |||
| 7784fc5c75 | |||
| 6dd017f33e | |||
| c8cd9f85f6 | |||
| d038bdb741 | |||
| f55e8d2c85 | |||
| f8dc1d9eae | |||
| 85393b0d40 | |||
| 75599ad20c | |||
| c6b948cbf5 | |||
| 4d42133a4b | |||
| 5b151805ff | |||
| aa86826bdb | |||
| 821373a340 | |||
| 8f37e293b1 | |||
| 0fb8ed0b53 | |||
| 0c696b2eb2 | |||
| cd127bc3f8 | |||
| 2cfc809490 | |||
| ba31dee16a | |||
| 146c743fb8 | |||
| 0c00e9ec2d | |||
| 49af55a2de | |||
| 0114b48197 | |||
| 09f615a5e6 | |||
| 9014acc548 | |||
| 4fb386be86 | |||
| ea8606be97 | |||
| e0527dc8ff | |||
| aaf2789a21 | |||
| f8dc64cc6b | |||
| ced5b751be | |||
| 8a60dda74e | |||
| c8c4df6ef7 | |||
| 0c0ac9dee5 | |||
| fdf6c91929 | |||
| 08d6e39a17 | |||
| b9bc7bd1b5 | |||
| 199d35d9c7 |
31
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Build & Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macOS-latest
|
||||
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: Upload artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: Build
|
||||
path: android/uhabits-android/build/outputs/
|
||||
40
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Build, Test & Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macOS-latest
|
||||
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
|
||||
51
.gitignore
vendored
@@ -1,35 +1,18 @@
|
||||
#built application files
|
||||
*.apk
|
||||
*.ap_
|
||||
|
||||
# files for the dex VM
|
||||
*.dex
|
||||
|
||||
# Java class files
|
||||
*.class
|
||||
|
||||
# generated files
|
||||
bin/
|
||||
gen/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Windows thumbnail db
|
||||
Thumbs.db
|
||||
|
||||
# OSX files
|
||||
.DS_Store
|
||||
|
||||
# Eclipse project files
|
||||
.classpath
|
||||
.project
|
||||
|
||||
# Android Studio
|
||||
.idea
|
||||
#.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
|
||||
.gradle
|
||||
build/
|
||||
*.iml
|
||||
|
||||
art/
|
||||
*.pbxuser
|
||||
*.perspective
|
||||
*.perspectivev3
|
||||
*.swp
|
||||
*~.nib
|
||||
.DS_Store
|
||||
._.DS_Store
|
||||
.externalNativeBuild
|
||||
.gradle
|
||||
.idea
|
||||
.secret
|
||||
build
|
||||
build/
|
||||
captures
|
||||
local.properties
|
||||
node_modules
|
||||
*xcuserdata*
|
||||
|
||||
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "libs/drag-sort-listview"]
|
||||
path = libs/drag-sort-listview
|
||||
url = https://github.com/iSoron/drag-sort-listview.git
|
||||
16
.secret/decrypt.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/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
Normal file
182
CHANGELOG.md
@@ -1,5 +1,185 @@
|
||||
# Changelog
|
||||
|
||||
### 1.8.8 (June 21, 2020)
|
||||
|
||||
* 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.8.0 (Jan 1, 2020)
|
||||
|
||||
* 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.7.11 (Aug 10, 2019)
|
||||
|
||||
* Fix bug that produced corrupted CSV files in some countries
|
||||
|
||||
### 1.7.10 (June 15, 2019)
|
||||
|
||||
* Fix bug that prevented some devices from showing notifications.
|
||||
* Update targetSdk to Android Pie (API level 28)
|
||||
|
||||
### 1.7.8 (April 21, 2018)
|
||||
|
||||
* Add support for adaptive icons (Oreo)
|
||||
* Add support for notification channels (Oreo)
|
||||
* Update translations
|
||||
|
||||
### 1.7.7 (September 30, 2017)
|
||||
|
||||
* Fix bug that caused reminders to show repeatedly on DST changes
|
||||
|
||||
### 1.7.6 (July 18, 2017)
|
||||
|
||||
* Fix bug that caused widgets not to render sometimes
|
||||
* Fix other minor bugs
|
||||
* Update translations
|
||||
|
||||
### 1.7.3 (May 30, 2017)
|
||||
|
||||
* Improve performance of 'sort by score'
|
||||
* Other minor bug fixes
|
||||
|
||||
### 1.7.2 (May 27, 2017)
|
||||
|
||||
* Fix crash at startup
|
||||
|
||||
### 1.7.1 (May 21, 2017)
|
||||
|
||||
* Fix crash (BadParcelableException)
|
||||
* Fix layout for RTL languages such as Arabic
|
||||
* Automatically detect and reject invalid database files
|
||||
* Add Hebrew translation
|
||||
|
||||
### 1.7.0 (Mar 31, 2017)
|
||||
|
||||
* 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.6.2 (Oct 13, 2016)
|
||||
|
||||
* Fix crash on Android 4.1
|
||||
|
||||
### 1.6.1 (Oct 10, 2016)
|
||||
|
||||
* Fix a crash at startup when database is corrupted
|
||||
|
||||
### 1.6.0 (Oct 10, 2016)
|
||||
|
||||
* 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 (Jun 19, 2016)
|
||||
|
||||
* Fix bug that prevented checkmark widget from working
|
||||
|
||||
### 1.5.5 (Jun 19, 2016)
|
||||
|
||||
* 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 (May 29, 2016)
|
||||
|
||||
* Fix crash upon opening settings screen in some phones
|
||||
* Fix missing folders in CSV archive
|
||||
* Add Serbian translation
|
||||
|
||||
### 1.5.3 (May 22, 2016)
|
||||
|
||||
* Complete Arabic and Czech translations
|
||||
* Fix crash at startup
|
||||
* Fix checkmark widget on custom launchers
|
||||
|
||||
### 1.5.2 (May 19, 2016)
|
||||
|
||||
* Fix missing attachment on bug reports
|
||||
* Fix bug that prevents some widgets from rendering
|
||||
* Complete Japanese translation
|
||||
|
||||
### 1.5.1 (May 17, 2016)
|
||||
|
||||
* Fix build on F-Droid
|
||||
|
||||
### 1.5.0 (May 15, 2016)
|
||||
|
||||
* 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 (April 9, 2016)
|
||||
|
||||
* Show error message on widgets, instead of crashing
|
||||
* Complete French translation
|
||||
* Minor fixes to other translations
|
||||
|
||||
### 1.4.0 (April 7, 2016)
|
||||
|
||||
* 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 (March 20, 2016)
|
||||
|
||||
* Add Spanish and Korean translations
|
||||
* Make small corrections to other translations
|
||||
* Fix incorrect date in history calendar
|
||||
|
||||
### 1.3.2 (March 18, 2016)
|
||||
|
||||
* Add Arabic, Italian, Polish, Russian and Swedish translations
|
||||
* Minor fixes to German and French translations
|
||||
* Minor bug fixes
|
||||
|
||||
### 1.3.1 (March 15, 2016)
|
||||
|
||||
* 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 (March 12, 2016)
|
||||
|
||||
* 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 (March 4, 2016)
|
||||
|
||||
* Ability to export habit data as CSV
|
||||
@@ -22,4 +202,4 @@
|
||||
|
||||
### 1.0.0 (February 19, 2016)
|
||||
|
||||
* Initial release
|
||||
* Initial release
|
||||
|
||||
207
NOTICE.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Copyright Notices
|
||||
|
||||
### ActiveAndroid
|
||||
|
||||
<https://github.com/pardom/ActiveAndroid>
|
||||
|
||||
Copyright (C) 2010 Michael Pardo
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
### Android Open Source Project
|
||||
|
||||
<https://source.android.com/>
|
||||
|
||||
Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
### FontAwesome
|
||||
|
||||
<http://fontawesome.io>
|
||||
|
||||
Font Awesome is a full suite of 605 pictographic icons for easy scalable
|
||||
vector graphics on websites, created and maintained by Dave Gandy. Licensed
|
||||
under the SIL OFL 1.1.
|
||||
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
### Material Design Icons
|
||||
|
||||
<https://github.com/google/material-design-icons>
|
||||
|
||||
Material design icons are the official icon set from Google that are designed
|
||||
under the material design guidelines. Available under the Creative Common
|
||||
Attribution 4.0 International License (CC-BY 4.0).
|
||||
|
||||
### Android Flow Layout
|
||||
|
||||
<https://github.com/ApmeM/android-flowlayout>
|
||||
|
||||
Extended linear layout that wrap its content when there is no place in the current line.
|
||||
|
||||
Copyright 2011, Artem Votincev (apmem.org)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
use this file except in compliance with the License. You may obtain a copy
|
||||
of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
### Dagger 2
|
||||
|
||||
<https://github.com/google/dagger>
|
||||
|
||||
A fast dependency injector for Android and Java.
|
||||
|
||||
Copyright 2012 Square, Inc.
|
||||
Copyright 2012 Google, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
### AutoFactory
|
||||
|
||||
<https://github.com/google/auto/tree/master/factory>
|
||||
|
||||
A source code generator for JSR-330-compatible factories.
|
||||
|
||||
Copyright 2013 Google, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
### Retrolambda
|
||||
|
||||
<https://github.com/orfjackal/retrolambda>
|
||||
|
||||
Backport of Java 8's lambda expressions to Java 7, 6 and 5
|
||||
|
||||
Copyright (c) 2013-2016 Esko Luontola and other Retrolambda contributors
|
||||
This software is released under the Apache License 2.0.
|
||||
The license text is at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
### PebbleKit SDK
|
||||
|
||||
<https://github.com/pebble/pebble-android-sdk/>
|
||||
|
||||
Android PebbleKit SDK to talk to the Pebble via Bluetooth
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2014 - 2015 Pebble Technology
|
||||
|
||||
### AppIntro
|
||||
|
||||
<https://github.com/PaoloRotolo/AppIntro>
|
||||
|
||||
Make a cool intro for your Android app.
|
||||
|
||||
Copyright 2015 Paolo Rotolo
|
||||
Copyright 2016 Maximilian Narr
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
### ButterKnife
|
||||
|
||||
<https://github.com/JakeWharton/butterknife>
|
||||
|
||||
Bind Android views and callbacks to fields and methods
|
||||
|
||||
Copyright 2013 Jake Wharton
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
### opencsv
|
||||
|
||||
<http://opencsv.sourceforge.net/>
|
||||
|
||||
Opencsv is a very simple csv (comma-separated values) parser library for Java.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
142
README.md
@@ -1,31 +1,14 @@
|
||||
# Loop Habit Tracker
|
||||
|
||||
Loop is a simple Android app that helps you create and maintain good habits, allowing you to achieve your long-term goals. Detailed graphs and statistics show you how your habits improved over time. It is completely ad-free and open source.
|
||||
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
|
||||
show you how your habits improved over time. It is completely ad-free and open
|
||||
source.
|
||||
|
||||
<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" width="200px"/></a>
|
||||
|
||||
## Features
|
||||
|
||||
<b>Simple, beautiful and modern interface</b>
|
||||
Loop has a minimalistic interface that is easy to use and follows the material design guidelines.
|
||||
|
||||
<b>Habit score</b>
|
||||
In addition to showing your current streak, Loop has an advanced algorithm for calculating the strength of your habits. Every repetition makes your habit stronger, and every missed day makes it weaker. A few missed days after a long streak, however, will not completely destroy your entire progress.
|
||||
|
||||
<b>Detailed graphs and statistics</b>
|
||||
Clearly see how your habits improved over time with beautiful and detailed graphs. Scroll back to see the complete history of your habits.
|
||||
|
||||
<b>Flexible schedules</b>
|
||||
Supports both daily habits and habits with more complex schedules, such as 3 times every week; one time every other week; or every other day.
|
||||
|
||||
<b>Reminders</b>
|
||||
Create an individual reminder for each habit, at a chosen hour of the day. Easily check, dismiss or snooze your habit directly from the notification, without opening the app.
|
||||
|
||||
<b>Optimized for smartwatches</b>
|
||||
Reminders can be checked, snoozed or dismissed directly from your Android Wear watch.
|
||||
|
||||
<b>Completely ad-free and open source</b>
|
||||
There are absolutely no advertisements, annoying notifications or intrusive permissions in this app, and there will never be. The complete source code is available under the GPLv3.
|
||||
<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>
|
||||
</p>
|
||||
|
||||
## Screenshots
|
||||
|
||||
@@ -34,14 +17,103 @@ There are absolutely no advertisements, annoying notifications or intrusive perm
|
||||
[![Habit strength][screen3th]][screen3]
|
||||
[![Habit history and streaks][screen4th]][screen4]
|
||||
[![Widgets][screen5th]][screen5]
|
||||
[![Night mode][screen6th]][screen6]
|
||||
|
||||
[screen1]: screenshots/original/uhabits1.png
|
||||
[screen2]: screenshots/original/uhabits2.png
|
||||
[screen3]: screenshots/original/uhabits3.png
|
||||
[screen4]: screenshots/original/uhabits4.png
|
||||
[screen5]: screenshots/original/uhabits5.png
|
||||
[screen1th]: screenshots/thumbs/uhabits1.png
|
||||
[screen2th]: screenshots/thumbs/uhabits2.png
|
||||
[screen3th]: screenshots/thumbs/uhabits3.png
|
||||
[screen4th]: screenshots/thumbs/uhabits4.png
|
||||
[screen5th]: screenshots/thumbs/uhabits5.png
|
||||
## Features
|
||||
|
||||
* <b>Beautiful, minimalistic and lightweight interface.</b>
|
||||
Loop has an elegant and minimalistic interface that is very easy to use, even for first-time users. Highly optimized for speed, the app works well even on older phones.
|
||||
|
||||
* <b>Habit score.</b>
|
||||
Loop has an advanced formula for calculating the strength of your habits. Every repetition makes your habit stronger and every missed day makes it weaker. A few missed days after a long streak, however, will not completely destroy your progress, unlike many other don't-break-the-chain apps.
|
||||
|
||||
* <b>Flexible schedules.</b>
|
||||
In addition to daily habits, Loop supports habits with more complex schedules, such as 3 times per week or every other day.
|
||||
|
||||
* <b>Reminders.</b>
|
||||
Schedule notifications to remind you of your habits. Each habit can have its own reminder, at a chosen time of the day. Easily check or dismiss your habit directly from the notification.
|
||||
|
||||
* <b>Widgets.</b>
|
||||
Be reminded of your habits whenever you unlock your phone. Colorful widgets allow you to track your habits directly from your home screen, without even opening the app.
|
||||
|
||||
* <b>Take control of your data.</b>
|
||||
If you want to further analyze your data, or move it to another service, Loop allows you to export it to spreadsheets (CSV) or to a database file (SQLite). For power users, checkmarks can be added through other apps, such as Tasker.
|
||||
|
||||
* <b>No limitations.</b>
|
||||
Track as many habits as you wish. Loop imposes no artificial limits on how many habits you can have. All features are available to all users. There are no in-app purchases.
|
||||
|
||||
* <b>Completely ad-free and open source.</b>
|
||||
There are no advertisements, annoying notifications or intrusive permissions in this app, and there will never be. The app is completely open-source (GPLv3).
|
||||
|
||||
* <b>Works offline and respects your privacy.</b>
|
||||
Loop doesn't require an Internet connection or online account registration. Your confidential data is never sent to anyone. Neither the developers nor any third-parties have access to it.
|
||||
|
||||
## Installing
|
||||
|
||||
The easiest way to install Loop is through the [Google Play Store][playstore] or [F-Droid][fdroid].
|
||||
You may also download and install the APK from the [releases page][releases];
|
||||
note, however, that the app will not be updated automatically. To build this
|
||||
app from the source code, see [build instructions][build].
|
||||
|
||||
## Contributing
|
||||
|
||||
Loop is an open source project developed entirely by volunteers. If you would
|
||||
like to contribute to the project, you are very welcome. There are many ways to
|
||||
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
|
||||
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
|
||||
and colleagues. You can also rate and review the app on Google Play Store, to help
|
||||
other users find it more easily.
|
||||
|
||||
* **Translate the app into your own language.** If you are not a native English
|
||||
speaker, and would like to see the app translated into your own language,
|
||||
please join our [open translation project][poedit]. If the translation
|
||||
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.
|
||||
|
||||
## License
|
||||
|
||||
<img align="right" src="https://www.gnu.org/graphics/gplv3-88x31.png">
|
||||
|
||||
Copyright (C) 2016-2019 Á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
|
||||
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/>.
|
||||
|
||||
[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
|
||||
[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
|
||||
[beta]: https://play.google.com/apps/testing/org.isoron.uhabits
|
||||
|
||||
28
android/.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
*.ap_
|
||||
*.apk
|
||||
*.class
|
||||
*.dex
|
||||
*.iml
|
||||
*.local
|
||||
*.local.*
|
||||
*.swp
|
||||
*.trace
|
||||
*~
|
||||
.DS_Store
|
||||
.classpath
|
||||
.gradle
|
||||
.idea
|
||||
.project
|
||||
.secret
|
||||
Thumbs.db
|
||||
art/
|
||||
bin/
|
||||
build/
|
||||
captures/
|
||||
docs/
|
||||
gen/
|
||||
local.properties
|
||||
crowdin.yaml
|
||||
local
|
||||
tmp/
|
||||
secret/
|
||||
1
android/android-base/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
31
android/android-base/build.gradle
Normal file
@@ -0,0 +1,31 @@
|
||||
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
Normal file
@@ -0,0 +1,25 @@
|
||||
# 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
|
||||
2
android/android-base/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<manifest package="org.isoron.androidbase"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"/>
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AppContext
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.*;
|
||||
|
||||
import dagger.*;
|
||||
|
||||
@Module
|
||||
public class AppContextModule
|
||||
{
|
||||
private final Context context;
|
||||
|
||||
public AppContextModule(@AppContext Context context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@AppContext
|
||||
Context getContext()
|
||||
{
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 java.lang.annotation.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ActivityContext
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 dagger.*;
|
||||
|
||||
@Module
|
||||
public class ActivityContextModule
|
||||
{
|
||||
private Context context;
|
||||
|
||||
public ActivityContextModule(Context context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ActivityContext
|
||||
public Context getContext()
|
||||
{
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 javax.inject.*;
|
||||
|
||||
/**
|
||||
* Scope used by objects that live as long as the activity is alive.
|
||||
*/
|
||||
@Scope
|
||||
public @interface ActivityScope { }
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 dagger.*;
|
||||
|
||||
@Module
|
||||
public class BaseActivityModule
|
||||
{
|
||||
private BaseActivity activity;
|
||||
|
||||
public BaseActivityModule(BaseActivity activity)
|
||||
{
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Provides
|
||||
public BaseActivity getBaseActivity()
|
||||
{
|
||||
return activity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
6
android/android-base/src/main/res/values/base.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<item name="toolbar" type="id" />
|
||||
<item name="toolbarShadow" type="id" />
|
||||
<item name="headerShadow" type="id" />
|
||||
<attr name="palette" format="reference"/>
|
||||
</resources>
|
||||
300
android/android-base/src/main/res/values/material_colors.xml
Normal file
@@ -0,0 +1,300 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="red_50">#FFEBEE</color>
|
||||
<color name="red_100">#FFCDD2</color>
|
||||
<color name="red_200">#EF9A9A</color>
|
||||
<color name="red_300">#E57373</color>
|
||||
<color name="red_400">#EF5350</color>
|
||||
<color name="red_500">#F44336</color>
|
||||
<color name="red_600">#E53935</color>
|
||||
<color name="red_700">#D32F2F</color>
|
||||
<color name="red_800">#C62828</color>
|
||||
<color name="red_900">#B71C1C</color>
|
||||
<color name="red_A100">#FF8A80</color>
|
||||
<color name="red_A200">#FF5252</color>
|
||||
<color name="red_A400">#FF1744</color>
|
||||
<color name="red_A700">#D50000</color>
|
||||
|
||||
<color name="deep_purple_50">#EDE7F6</color>
|
||||
<color name="deep_purple_100">#D1C4E9</color>
|
||||
<color name="deep_purple_200">#B39DDB</color>
|
||||
<color name="deep_purple_300">#9575CD</color>
|
||||
<color name="deep_purple_400">#7E57C2</color>
|
||||
<color name="deep_purple_500">#673AB7</color>
|
||||
<color name="deep_purple_600">#5E35B1</color>
|
||||
<color name="deep_purple_700">#512DA8</color>
|
||||
<color name="deep_purple_800">#4527A0</color>
|
||||
<color name="deep_purple_900">#311B92</color>
|
||||
<color name="deep_purple_A100">#B388FF</color>
|
||||
<color name="deep_purple_A200">#7C4DFF</color>
|
||||
<color name="deep_purple_A400">#651FFF</color>
|
||||
<color name="deep_purple_A700">#6200EA</color>
|
||||
|
||||
<color name="light_blue_50">#E1F5FE</color>
|
||||
<color name="light_blue_100">#B3E5FC</color>
|
||||
<color name="light_blue_200">#81D4FA</color>
|
||||
<color name="light_blue_300">#4FC3F7</color>
|
||||
<color name="light_blue_400">#29B6F6</color>
|
||||
<color name="light_blue_500">#03A9F4</color>
|
||||
<color name="light_blue_600">#039BE5</color>
|
||||
<color name="light_blue_700">#0288D1</color>
|
||||
<color name="light_blue_800">#0277BD</color>
|
||||
<color name="light_blue_900">#01579B</color>
|
||||
<color name="light_blue_A100">#80D8FF</color>
|
||||
<color name="light_blue_A200">#40C4FF</color>
|
||||
<color name="light_blue_A400">#00B0FF</color>
|
||||
<color name="light_blue_A700">#0091EA</color>
|
||||
|
||||
<color name="green_50">#E8F5E9</color>
|
||||
<color name="green_100">#C8E6C9</color>
|
||||
<color name="green_200">#A5D6A7</color>
|
||||
<color name="green_300">#81C784</color>
|
||||
<color name="green_400">#66BB6A</color>
|
||||
<color name="green_500">#4CAF50</color>
|
||||
<color name="green_600">#43A047</color>
|
||||
<color name="green_700">#388E3C</color>
|
||||
<color name="green_800">#2E7D32</color>
|
||||
<color name="green_900">#1B5E20</color>
|
||||
<color name="green_A100">#B9F6CA</color>
|
||||
<color name="green_A200">#69F0AE</color>
|
||||
<color name="green_A400">#00E676</color>
|
||||
<color name="green_A700">#00C853</color>
|
||||
|
||||
<color name="yellow_50">#FFFDE7</color>
|
||||
<color name="yellow_100">#FFF9C4</color>
|
||||
<color name="yellow_200">#FFF59D</color>
|
||||
<color name="yellow_300">#FFF176</color>
|
||||
<color name="yellow_400">#FFEE58</color>
|
||||
<color name="yellow_500">#FFEB3B</color>
|
||||
<color name="yellow_600">#FDD835</color>
|
||||
<color name="yellow_700">#FBC02D</color>
|
||||
<color name="yellow_800">#F9A825</color>
|
||||
<color name="yellow_900">#F57F17</color>
|
||||
<color name="yellow_A100">#FFFF8D</color>
|
||||
<color name="yellow_A200">#FFFF00</color>
|
||||
<color name="yellow_A400">#FFEA00</color>
|
||||
<color name="yellow_A700">#FFD600</color>
|
||||
|
||||
<color name="deep_orange_50">#FBE9E7</color>
|
||||
<color name="deep_orange_100">#FFCCBC</color>
|
||||
<color name="deep_orange_200">#FFAB91</color>
|
||||
<color name="deep_orange_300">#FF8A65</color>
|
||||
<color name="deep_orange_400">#FF7043</color>
|
||||
<color name="deep_orange_500">#FF5722</color>
|
||||
<color name="deep_orange_600">#F4511E</color>
|
||||
<color name="deep_orange_700">#E64A19</color>
|
||||
<color name="deep_orange_800">#D84315</color>
|
||||
<color name="deep_orange_900">#BF360C</color>
|
||||
<color name="deep_orange_A100">#FF9E80</color>
|
||||
<color name="deep_orange_A200">#FF6E40</color>
|
||||
<color name="deep_orange_A400">#FF3D00</color>
|
||||
<color name="deep_orange_A700">#DD2C00</color>
|
||||
|
||||
<color name="blue_grey_50">#ECEFF1</color>
|
||||
<color name="blue_grey_100">#CFD8DC</color>
|
||||
<color name="blue_grey_200">#B0BEC5</color>
|
||||
<color name="blue_grey_300">#90A4AE</color>
|
||||
<color name="blue_grey_400">#78909C</color>
|
||||
<color name="blue_grey_500">#607D8B</color>
|
||||
<color name="blue_grey_600">#546E7A</color>
|
||||
<color name="blue_grey_700">#455A64</color>
|
||||
<color name="blue_grey_800">#37474F</color>
|
||||
<color name="blue_grey_900">#263238</color>
|
||||
|
||||
<color name="pink_50">#FCE4EC</color>
|
||||
<color name="pink_100">#F8BBD0</color>
|
||||
<color name="pink_200">#F48FB1</color>
|
||||
<color name="pink_300">#F06292</color>
|
||||
<color name="pink_400">#EC407A</color>
|
||||
<color name="pink_500">#E91E63</color>
|
||||
<color name="pink_600">#D81B60</color>
|
||||
<color name="pink_700">#C2185B</color>
|
||||
<color name="pink_800">#AD1457</color>
|
||||
<color name="pink_900">#880E4F</color>
|
||||
<color name="pink_A100">#FF80AB</color>
|
||||
<color name="pink_A200">#FF4081</color>
|
||||
<color name="pink_A400">#F50057</color>
|
||||
<color name="pink_A700">#C51162</color>
|
||||
|
||||
<color name="indigo_50">#E8EAF6</color>
|
||||
<color name="indigo_100">#C5CAE9</color>
|
||||
<color name="indigo_200">#9FA8DA</color>
|
||||
<color name="indigo_300">#7986CB</color>
|
||||
<color name="indigo_400">#5C6BC0</color>
|
||||
<color name="indigo_500">#3F51B5</color>
|
||||
<color name="indigo_600">#3949AB</color>
|
||||
<color name="indigo_700">#303F9F</color>
|
||||
<color name="indigo_800">#283593</color>
|
||||
<color name="indigo_900">#1A237E</color>
|
||||
<color name="indigo_A100">#8C9EFF</color>
|
||||
<color name="indigo_A200">#536DFE</color>
|
||||
<color name="indigo_A400">#3D5AFE</color>
|
||||
<color name="indigo_A700">#304FFE</color>
|
||||
|
||||
<color name="cyan_50">#E0F7FA</color>
|
||||
<color name="cyan_100">#B2EBF2</color>
|
||||
<color name="cyan_200">#80DEEA</color>
|
||||
<color name="cyan_300">#4DD0E1</color>
|
||||
<color name="cyan_400">#26C6DA</color>
|
||||
<color name="cyan_500">#00BCD4</color>
|
||||
<color name="cyan_600">#00ACC1</color>
|
||||
<color name="cyan_700">#0097A7</color>
|
||||
<color name="cyan_800">#00838F</color>
|
||||
<color name="cyan_900">#006064</color>
|
||||
<color name="cyan_A100">#84FFFF</color>
|
||||
<color name="cyan_A200">#18FFFF</color>
|
||||
<color name="cyan_A400">#00E5FF</color>
|
||||
<color name="cyan_A700">#00B8D4</color>
|
||||
|
||||
<color name="light_green_50">#F1F8E9</color>
|
||||
<color name="light_green_100">#DCEDC8</color>
|
||||
<color name="light_green_200">#C5E1A5</color>
|
||||
<color name="light_green_300">#AED581</color>
|
||||
<color name="light_green_400">#9CCC65</color>
|
||||
<color name="light_green_500">#8BC34A</color>
|
||||
<color name="light_green_600">#7CB342</color>
|
||||
<color name="light_green_700">#689F38</color>
|
||||
<color name="light_green_800">#558B2F</color>
|
||||
<color name="light_green_900">#33691E</color>
|
||||
<color name="light_green_A100">#CCFF90</color>
|
||||
<color name="light_green_A200">#B2FF59</color>
|
||||
<color name="light_green_A400">#76FF03</color>
|
||||
<color name="light_green_A700">#64DD17</color>
|
||||
|
||||
<color name="amber_50">#FFF8E1</color>
|
||||
<color name="amber_100">#FFECB3</color>
|
||||
<color name="amber_200">#FFE082</color>
|
||||
<color name="amber_300">#FFD54F</color>
|
||||
<color name="amber_400">#FFCA28</color>
|
||||
<color name="amber_500">#FFC107</color>
|
||||
<color name="amber_600">#FFB300</color>
|
||||
<color name="amber_700">#FFA000</color>
|
||||
<color name="amber_800">#FF8F00</color>
|
||||
<color name="amber_900">#FF6F00</color>
|
||||
<color name="amber_A100">#FFE57F</color>
|
||||
<color name="amber_A200">#FFD740</color>
|
||||
<color name="amber_A400">#FFC400</color>
|
||||
<color name="amber_A700">#FFAB00</color>
|
||||
|
||||
<color name="brown_50">#EFEBE9</color>
|
||||
<color name="brown_100">#D7CCC8</color>
|
||||
<color name="brown_200">#BCAAA4</color>
|
||||
<color name="brown_300">#A1887F</color>
|
||||
<color name="brown_400">#8D6E63</color>
|
||||
<color name="brown_500">#795548</color>
|
||||
<color name="brown_600">#6D4C41</color>
|
||||
<color name="brown_700">#5D4037</color>
|
||||
<color name="brown_800">#4E342E</color>
|
||||
<color name="brown_900">#3E2723</color>
|
||||
|
||||
<color name="purple_50">#F3E5F5</color>
|
||||
<color name="purple_100">#E1BEE7</color>
|
||||
<color name="purple_200">#CE93D8</color>
|
||||
<color name="purple_300">#BA68C8</color>
|
||||
<color name="purple_400">#AB47BC</color>
|
||||
<color name="purple_500">#9C27B0</color>
|
||||
<color name="purple_600">#8E24AA</color>
|
||||
<color name="purple_700">#7B1FA2</color>
|
||||
<color name="purple_800">#6A1B9A</color>
|
||||
<color name="purple_900">#4A148C</color>
|
||||
<color name="purple_A100">#EA80FC</color>
|
||||
<color name="purple_A200">#E040FB</color>
|
||||
<color name="purple_A400">#D500F9</color>
|
||||
<color name="purple_A700">#AA00FF</color>
|
||||
|
||||
<color name="blue_50">#E3F2FD</color>
|
||||
<color name="blue_100">#BBDEFB</color>
|
||||
<color name="blue_200">#90CAF9</color>
|
||||
<color name="blue_300">#64B5F6</color>
|
||||
<color name="blue_400">#42A5F5</color>
|
||||
<color name="blue_500">#2196F3</color>
|
||||
<color name="blue_600">#1E88E5</color>
|
||||
<color name="blue_700">#1976D2</color>
|
||||
<color name="blue_800">#1565C0</color>
|
||||
<color name="blue_900">#0D47A1</color>
|
||||
<color name="blue_A100">#82B1FF</color>
|
||||
<color name="blue_A200">#448AFF</color>
|
||||
<color name="blue_A400">#2979FF</color>
|
||||
<color name="blue_A700">#2962FF</color>
|
||||
|
||||
<color name="teal_50">#E0F2F1</color>
|
||||
<color name="teal_100">#B2DFDB</color>
|
||||
<color name="teal_200">#80CBC4</color>
|
||||
<color name="teal_300">#4DB6AC</color>
|
||||
<color name="teal_400">#26A69A</color>
|
||||
<color name="teal_500">#009688</color>
|
||||
<color name="teal_600">#00897B</color>
|
||||
<color name="teal_700">#00796B</color>
|
||||
<color name="teal_800">#00695C</color>
|
||||
<color name="teal_900">#004D40</color>
|
||||
<color name="teal_A100">#A7FFEB</color>
|
||||
<color name="teal_A200">#64FFDA</color>
|
||||
<color name="teal_A400">#1DE9B6</color>
|
||||
<color name="teal_A700">#00BFA5</color>
|
||||
|
||||
<color name="lime_50">#F9FBE7</color>
|
||||
<color name="lime_100">#F0F4C3</color>
|
||||
<color name="lime_200">#E6EE9C</color>
|
||||
<color name="lime_300">#DCE775</color>
|
||||
<color name="lime_400">#D4E157</color>
|
||||
<color name="lime_500">#CDDC39</color>
|
||||
<color name="lime_600">#C0CA33</color>
|
||||
<color name="lime_700">#AFB42B</color>
|
||||
<color name="lime_800">#9E9D24</color>
|
||||
<color name="lime_900">#827717</color>
|
||||
<color name="lime_A100">#F4FF81</color>
|
||||
<color name="lime_A200">#EEFF41</color>
|
||||
<color name="lime_A400">#C6FF00</color>
|
||||
<color name="lime_A700">#AEEA00</color>
|
||||
|
||||
<color name="orange_50">#FFF3E0</color>
|
||||
<color name="orange_100">#FFE0B2</color>
|
||||
<color name="orange_200">#FFCC80</color>
|
||||
<color name="orange_300">#FFB74D</color>
|
||||
<color name="orange_400">#FFA726</color>
|
||||
<color name="orange_500">#FF9800</color>
|
||||
<color name="orange_600">#FB8C00</color>
|
||||
<color name="orange_700">#F57C00</color>
|
||||
<color name="orange_800">#EF6C00</color>
|
||||
<color name="orange_900">#E65100</color>
|
||||
<color name="orange_A100">#FFD180</color>
|
||||
<color name="orange_A200">#FFAB40</color>
|
||||
<color name="orange_A400">#FF9100</color>
|
||||
<color name="orange_A700">#FF6D00</color>
|
||||
|
||||
<color name="grey_50">#FAFAFA</color>
|
||||
<color name="grey_100">#F5F5F5</color>
|
||||
<color name="grey_200">#EEEEEE</color>
|
||||
<color name="grey_300">#E0E0E0</color>
|
||||
<color name="grey_400">#BDBDBD</color>
|
||||
<color name="grey_500">#9E9E9E</color>
|
||||
<color name="grey_600">#757575</color>
|
||||
<color name="grey_700">#616161</color>
|
||||
<color name="grey_750">#525252</color>
|
||||
<color name="grey_800">#424242</color>
|
||||
<color name="grey_850">#303030</color>
|
||||
<color name="grey_875">#282828</color>
|
||||
<color name="grey_900">#212121</color>
|
||||
<color name="grey_950">#101010</color>
|
||||
|
||||
<color name="white">#ffffff</color>
|
||||
<color name="black">#000000</color>
|
||||
|
||||
<color name="black_ae">#ef000000</color>
|
||||
<color name="black_ac">#cf000000</color>
|
||||
<color name="black_aa">#af000000</color>
|
||||
<color name="black_a8">#8f000000</color>
|
||||
<color name="black_a6">#6f000000</color>
|
||||
<color name="black_a4">#4f000000</color>
|
||||
<color name="black_a2">#2f000000</color>
|
||||
<color name="black_a0">#0f000000</color>
|
||||
|
||||
<color name="white_ae">#efffffff</color>
|
||||
<color name="white_ac">#cfffffff</color>
|
||||
<color name="white_aa">#afffffff</color>
|
||||
<color name="white_a8">#8fffffff</color>
|
||||
<color name="white_a6">#6fffffff</color>
|
||||
<color name="white_a4">#4fffffff</color>
|
||||
<color name="white_a2">#2fffffff</color>
|
||||
<color name="white_a0">#0fffffff</color>
|
||||
</resources>
|
||||
1
android/android-pickers/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
24
android/android-pickers/build.gradle
Normal file
@@ -0,0 +1,24 @@
|
||||
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
Normal file
@@ -0,0 +1,25 @@
|
||||
# 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
|
||||
2
android/android-pickers/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<manifest package="com.android"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"/>
|
||||
@@ -16,24 +16,21 @@
|
||||
|
||||
package com.android.colorpicker;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import android.app.*;
|
||||
import android.os.*;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.*;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.android.colorpicker.ColorPickerSwatch.OnColorSelectedListener;
|
||||
import com.android.*;
|
||||
import com.android.colorpicker.ColorPickerSwatch.*;
|
||||
|
||||
/**
|
||||
* A dialog which takes in as input an array of palette and creates a palette allowing the user to
|
||||
* select a specific color swatch, which invokes a listener.
|
||||
*/
|
||||
public class ColorPickerDialog extends DialogFragment implements OnColorSelectedListener {
|
||||
public class ColorPickerDialog extends AppCompatDialogFragment implements OnColorSelectedListener {
|
||||
|
||||
public static final int SIZE_LARGE = 1;
|
||||
public static final int SIZE_SMALL = 2;
|
||||
@@ -16,18 +16,14 @@
|
||||
|
||||
package com.android.colorpicker;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TableLayout;
|
||||
import android.widget.TableRow;
|
||||
|
||||
import com.android.colorpicker.ColorPickerSwatch.OnColorSelectedListener;
|
||||
import com.android.*;
|
||||
import com.android.colorpicker.ColorPickerSwatch.*;
|
||||
|
||||
/**
|
||||
* A color picker custom view which creates an grid of color squares. The number of squares per
|
||||
@@ -16,14 +16,12 @@
|
||||
|
||||
package com.android.colorpicker;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import android.content.*;
|
||||
import android.graphics.drawable.*;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import com.android.*;
|
||||
|
||||
/**
|
||||
* Creates a circular swatch of a specified color. Adds a checkmark if marked as checked.
|
||||
@@ -16,36 +16,23 @@
|
||||
|
||||
package com.android.datetimepicker.date;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import android.animation.*;
|
||||
import android.app.*;
|
||||
import android.content.res.*;
|
||||
import android.os.*;
|
||||
import android.text.format.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
import android.view.View.*;
|
||||
import android.view.animation.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import com.android.*;
|
||||
import com.android.datetimepicker.*;
|
||||
import com.android.datetimepicker.date.MonthAdapter.*;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.app.Activity;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.datetimepicker.HapticFeedbackController;
|
||||
import com.android.datetimepicker.Utils;
|
||||
import com.android.datetimepicker.date.MonthAdapter.CalendarDay;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Dialog allowing users to select a date.
|
||||
@@ -16,37 +16,27 @@
|
||||
|
||||
package com.android.datetimepicker.date;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.Calendar;
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.graphics.*;
|
||||
import android.graphics.Paint.*;
|
||||
import android.os.*;
|
||||
import androidx.core.view.*;
|
||||
import androidx.core.view.accessibility.*;
|
||||
import androidx.core.widget.*;
|
||||
import android.text.format.*;
|
||||
import android.view.*;
|
||||
import android.view.accessibility.*;
|
||||
|
||||
import androidx.customview.widget.ExploreByTouchHelper;
|
||||
|
||||
import com.android.*;
|
||||
import com.android.datetimepicker.*;
|
||||
import com.android.datetimepicker.date.MonthAdapter.*;
|
||||
|
||||
import java.security.*;
|
||||
import java.util.*;
|
||||
import java.util.Formatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Align;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import android.support.v4.widget.ExploreByTouchHelper;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Time;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.android.datetimepicker.Utils;
|
||||
import com.android.datetimepicker.date.MonthAdapter.CalendarDay;
|
||||
|
||||
/**
|
||||
* A calendar-like view displaying a specified month and the appropriate selectable day numbers
|
||||
@@ -16,16 +16,14 @@
|
||||
|
||||
package com.android.datetimepicker.date;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.graphics.*;
|
||||
import android.graphics.Paint.*;
|
||||
import android.util.*;
|
||||
import android.widget.*;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Align;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
import com.android.*;
|
||||
|
||||
/**
|
||||
* A text view which, when pressed or activated, displays a blue circle around the text.
|
||||
@@ -16,24 +16,18 @@
|
||||
|
||||
package com.android.datetimepicker.date;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.graphics.drawable.*;
|
||||
import android.view.*;
|
||||
import android.view.accessibility.*;
|
||||
import android.widget.*;
|
||||
import android.widget.AdapterView.*;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import com.android.*;
|
||||
import com.android.datetimepicker.date.DatePickerDialog.*;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.datetimepicker.date.DatePickerDialog.OnDateChangedListener;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Displays a selectable list of years.
|
||||
@@ -16,20 +16,17 @@
|
||||
|
||||
package com.android.datetimepicker.time;
|
||||
|
||||
import java.text.DateFormatSymbols;
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.graphics.*;
|
||||
import android.graphics.Paint.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import com.android.*;
|
||||
import com.android.datetimepicker.*;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Align;
|
||||
import android.graphics.Typeface;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.datetimepicker.Utils;
|
||||
import java.text.*;
|
||||
|
||||
/**
|
||||
* Draw the two smaller AM and PM circles next to where the larger circle will be.
|
||||
@@ -16,14 +16,14 @@
|
||||
|
||||
package com.android.datetimepicker.time;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.graphics.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
|
||||
import com.android.*;
|
||||
|
||||
/**
|
||||
* Draws a simple white circle on which the numbers will be drawn.
|
||||
@@ -16,30 +16,20 @@
|
||||
|
||||
package com.android.datetimepicker.time;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import android.animation.*;
|
||||
import android.annotation.*;
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.os.*;
|
||||
import android.text.format.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
import android.view.View.*;
|
||||
import android.view.accessibility.*;
|
||||
import android.widget.*;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Time;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.datetimepicker.HapticFeedbackController;
|
||||
import com.android.*;
|
||||
import com.android.datetimepicker.*;
|
||||
|
||||
/**
|
||||
* The primary layout to hold the circular picker, and the am/pm buttons. This view well measure
|
||||
@@ -16,21 +16,16 @@
|
||||
|
||||
package com.android.datetimepicker.time;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import android.animation.*;
|
||||
import android.animation.ValueAnimator.*;
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.graphics.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
|
||||
import android.animation.Keyframe;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.datetimepicker.Utils;
|
||||
import com.android.*;
|
||||
import com.android.datetimepicker.*;
|
||||
|
||||
/**
|
||||
* View to show what number is selected. This will draw a blue circle over the number, with a blue
|
||||
@@ -16,21 +16,16 @@
|
||||
|
||||
package com.android.datetimepicker.time;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import android.animation.*;
|
||||
import android.animation.ValueAnimator.*;
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.graphics.*;
|
||||
import android.graphics.Paint.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
|
||||
import android.animation.Keyframe;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Align;
|
||||
import android.graphics.Typeface;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import com.android.*;
|
||||
|
||||
/**
|
||||
* A view to show a series of numbers in a circular pattern.
|
||||
@@ -16,40 +16,30 @@
|
||||
|
||||
package com.android.datetimepicker.time;
|
||||
|
||||
import java.text.DateFormatSymbols;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import android.animation.*;
|
||||
import android.annotation.*;
|
||||
import android.app.ActionBar.*;
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.os.*;
|
||||
import androidx.appcompat.app.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
import android.view.View.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import com.android.*;
|
||||
import com.android.datetimepicker.*;
|
||||
import com.android.datetimepicker.time.RadialPickerLayout.*;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActionBar.LayoutParams;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnKeyListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.datetimepicker.HapticFeedbackController;
|
||||
import com.android.datetimepicker.Utils;
|
||||
import com.android.datetimepicker.time.RadialPickerLayout.OnValueSelectedListener;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Dialog to set a time.
|
||||
*/
|
||||
public class TimePickerDialog extends DialogFragment implements OnValueSelectedListener{
|
||||
public class TimePickerDialog extends AppCompatDialogFragment implements OnValueSelectedListener{
|
||||
private static final String TAG = "TimePickerDialog";
|
||||
|
||||
private static final String KEY_HOUR_OF_DAY = "hour_of_day";
|
||||
@@ -73,6 +63,7 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
|
||||
private static final int PULSE_ANIMATOR_DELAY = 300;
|
||||
|
||||
private OnTimeSetListener mCallback;
|
||||
private DialogInterface.OnDismissListener dismissListener;
|
||||
|
||||
private HapticFeedbackController mHapticFeedbackController;
|
||||
|
||||
@@ -126,7 +117,7 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
|
||||
*/
|
||||
void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute);
|
||||
|
||||
void onTimeCleared(RadialPickerLayout view);
|
||||
default void onTimeCleared(RadialPickerLayout view) {}
|
||||
}
|
||||
|
||||
public TimePickerDialog() {
|
||||
@@ -192,10 +183,16 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState)
|
||||
{
|
||||
return new AppCompatDialog(getActivity(), R.style.TimePickerDialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
View view = inflater.inflate(R.layout.time_picker_dialog, null);
|
||||
KeyboardListener keyboardListener = new KeyboardListener();
|
||||
@@ -1002,4 +999,15 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setDismissListener( DialogInterface.OnDismissListener listener ) {
|
||||
dismissListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
super.onDismiss(dialog);
|
||||
if( dismissListener != null )
|
||||
dismissListener.onDismiss(dialog);
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
149
android/android-pickers/src/main/res/values/pickers.xml
Normal file
@@ -0,0 +1,149 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<dimen name="color_swatch_large">64dip</dimen>
|
||||
<dimen name="color_swatch_small">48dip</dimen>
|
||||
<dimen name="color_swatch_margins_large">8dip</dimen>
|
||||
<dimen name="color_swatch_margins_small">4dip</dimen>
|
||||
|
||||
<item name="circle_radius_multiplier" format="float" translatable="false" type="string">
|
||||
0.82
|
||||
</item>
|
||||
<item name="circle_radius_multiplier_24HourMode" format="float" translatable="false" type="string">
|
||||
0.85
|
||||
</item>
|
||||
<item name="selection_radius_multiplier" format="float" translatable="false" type="string">
|
||||
0.16
|
||||
</item>
|
||||
<item name="ampm_circle_radius_multiplier" format="float" translatable="false" type="string">
|
||||
0.19
|
||||
</item>
|
||||
<item name="numbers_radius_multiplier_normal" format="float" translatable="false" type="string">
|
||||
0.81
|
||||
</item>
|
||||
<item name="numbers_radius_multiplier_inner" format="float" translatable="false" type="string">
|
||||
0.60
|
||||
</item>
|
||||
<item name="numbers_radius_multiplier_outer" format="float" translatable="false" type="string">
|
||||
0.83
|
||||
</item>
|
||||
<item name="text_size_multiplier_normal" format="float" translatable="false" type="string">
|
||||
0.17
|
||||
</item>
|
||||
<item name="text_size_multiplier_inner" format="float" translatable="false" type="string">
|
||||
0.14
|
||||
</item>
|
||||
<item name="text_size_multiplier_outer" format="float" translatable="false" type="string">
|
||||
0.11
|
||||
</item>
|
||||
|
||||
<dimen name="time_label_size">60sp</dimen>
|
||||
<dimen name="extra_time_label_margin">-30dp</dimen>
|
||||
<dimen name="ampm_label_size">16sp</dimen>
|
||||
<dimen name="done_label_size">14sp</dimen>
|
||||
<dimen name="ampm_left_padding">6dip</dimen>
|
||||
<dimen name="separator_padding">4dip</dimen>
|
||||
<dimen name="header_height">80dip</dimen>
|
||||
<dimen name="footer_height">48dip</dimen>
|
||||
<dimen name="minimum_margin_sides">48dip</dimen>
|
||||
<dimen name="minimum_margin_top_bottom">24dip</dimen>
|
||||
<dimen name="picker_dimen">250dip</dimen>
|
||||
<dimen name="date_picker_component_width">250dp</dimen>
|
||||
<dimen name="date_picker_header_height">30dp</dimen>
|
||||
<dimen name="selected_calendar_layout_height">155dp</dimen>
|
||||
<dimen name="date_picker_view_animator_height">270dp</dimen>
|
||||
<dimen name="done_button_height">42dp</dimen>
|
||||
<dimen name="month_list_item_header_height">50dp</dimen>
|
||||
<dimen name="month_day_label_text_size">10sp</dimen>
|
||||
<dimen name="day_number_select_circle_radius">16dp</dimen>
|
||||
<dimen name="month_select_circle_radius">45dp</dimen>
|
||||
<dimen name="selected_date_year_size">30dp</dimen>
|
||||
<dimen name="selected_date_day_size">75dp</dimen>
|
||||
<dimen name="selected_date_month_size">30dp</dimen>
|
||||
<dimen name="date_picker_header_text_size">14dp</dimen>
|
||||
<dimen name="month_label_size">16sp</dimen>
|
||||
<dimen name="day_number_size">16sp</dimen>
|
||||
<dimen name="year_label_height">64dp</dimen>
|
||||
<dimen name="year_label_text_size">22dp</dimen>
|
||||
|
||||
<string name="color_swatch_description" translatable="false">Color <xliff:g example="14" id="color_index">%1$d</xliff:g></string>
|
||||
<string name="color_swatch_description_selected" translatable="false">Color <xliff:g example="14" id="color_index">%1$d</xliff:g> selected</string>
|
||||
|
||||
<!-- Date and time picker -->
|
||||
<string name="hour_picker_description" translatable="false">Hours circular slider</string>
|
||||
<string name="minute_picker_description" translatable="false">Minutes circular slider</string>
|
||||
<string name="day_picker_description" translatable="false">Month grid of days</string>
|
||||
<string name="year_picker_description" translatable="false">Year list</string>
|
||||
<string name="select_day" translatable="false">Select month and day</string>
|
||||
<string name="select_year" translatable="false">Select year</string>
|
||||
<string name="item_is_selected" translatable="false"><xliff:g example="2013" id="item">%1$s</xliff:g> selected</string>
|
||||
<string name="deleted_key" translatable="false"><xliff:g example="4" id="key">%1$s</xliff:g> deleted</string>
|
||||
<string name="time_placeholder" translatable="false">--</string>
|
||||
<string name="time_separator" translatable="false">:</string>
|
||||
<string name="radial_numbers_typeface" translatable="false">sans-serif</string>
|
||||
<string name="sans_serif" translatable="false">sans-serif</string>
|
||||
<string name="day_of_week_label_typeface" translatable="false">sans-serif</string>
|
||||
|
||||
<!-- Time and Date picker -->
|
||||
<color name="circle_background">#f2f2f2</color>
|
||||
<color name="line_background">#cccccc</color>
|
||||
<color name="ampm_text_color">#8c8c8c</color>
|
||||
<color name="done_text_color_normal">#000000</color>
|
||||
<color name="done_text_color_disabled">#cccccc</color>
|
||||
<color name="numbers_text_color">#8c8c8c</color>
|
||||
<color name="transparent">#00000000</color>
|
||||
<color name="transparent_black">#7f000000</color>
|
||||
<color name="blue">#33b5e5</color>
|
||||
<color name="blue_focused">#c1e8f7</color>
|
||||
<color name="neutral_pressed">#33999999</color>
|
||||
<color name="darker_blue">#0099cc</color>
|
||||
<color name="date_picker_text_normal">#ff999999</color>
|
||||
<color name="calendar_header">#999999</color>
|
||||
<color name="date_picker_view_animator">#f2f2f2</color>
|
||||
<color name="calendar_selected_date_text">#ffd1d2d4</color>
|
||||
<color name="done_text_color">#888888</color>
|
||||
<color name="done_text_color_dark">#888888</color>
|
||||
<color name="white">#ffffff</color>
|
||||
<color name="black">#000000</color>
|
||||
|
||||
<!-- Colors for red theme -->
|
||||
<color name="red">#ff3333</color>
|
||||
<color name="red_focused">#853333</color>
|
||||
<color name="light_gray">#404040</color>
|
||||
<color name="dark_gray">#363636</color>
|
||||
<color name="line_dark">#808080</color>
|
||||
|
||||
<style name="time_label">
|
||||
<item name="android:textSize">@dimen/time_label_size</item>
|
||||
<item name="android:textColor">@color/numbers_text_color</item>
|
||||
</style>
|
||||
|
||||
<style name="ampm_label">
|
||||
<item name="android:textSize">@dimen/ampm_label_size</item>
|
||||
<item name="android:textAllCaps">true</item>
|
||||
<item name="android:textColor">@color/ampm_text_color</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="TimePickerDialog" parent="@style/Theme.AppCompat.Light.Dialog">
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
9
android/android-pickers/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?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>
|
||||
21
android/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
||||
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
Executable file
@@ -0,0 +1,279 @@
|
||||
#!/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
|
||||
18
android/gradle.properties
Normal file
@@ -0,0 +1,18 @@
|
||||
VERSION_CODE = 51
|
||||
VERSION_NAME = 1.8.8
|
||||
|
||||
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
|
||||
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
@@ -1,6 +1,5 @@
|
||||
#Wed Apr 10 15:27:10 PDT 2013
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
|
||||
183
android/gradlew
vendored
Executable file
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
100
android/gradlew.bat
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
1
android/play
Symbolic link
@@ -0,0 +1 @@
|
||||
uhabits-android/src/main/play/
|
||||
1
android/settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
include ':uhabits-android', ':uhabits-core', ':android-base', ':android-pickers'
|
||||
BIN
android/tools/automator-log-converter-1.5.0.jar
Normal file
157
android/tools/coverage-badge/badge.py
Normal file
@@ -0,0 +1,157 @@
|
||||
"""
|
||||
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()
|
||||
21
android/tools/coverage-badge/templates/flat.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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>
|
||||
|
After Width: | Height: | Size: 926 B |
BIN
android/tools/test-butler-app-2.0.2.apk
Normal file
137
android/uhabits-android/build.gradle
Normal file
@@ -0,0 +1,137 @@
|
||||
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"
|
||||
}
|
||||