Compare commits
585 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 83c1ab35d5 | |||
| 7a6563736a | |||
| 55c50c1119 | |||
| ba08968600 | |||
| 79459c373e | |||
| 590298bf5b | |||
| 09bf49a9ce | |||
| 38fb37cde2 | |||
| d0b4e3e163 | |||
| 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 | |||
| 45fd8a29e1 | |||
| 8c4fab28aa | |||
| e6e80b9841 | |||
| 442c3ed78d | |||
| 2399dccddc | |||
| dd5f37290c | |||
| 5b402478e9 | |||
| e3b7e9f60f | |||
| b0040bd83c | |||
| 56e1268f85 | |||
| 41d9e2f0f5 | |||
| 1fcfb9b22e |
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/
|
||||||
42
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
name: Build, Test & Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: macOS-latest
|
||||||
|
env:
|
||||||
|
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Install GPG
|
||||||
|
uses: olafurpg/setup-gpg@v2
|
||||||
|
- name: Decrypt secrets
|
||||||
|
env:
|
||||||
|
GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }}
|
||||||
|
run: .secret/decrypt.sh
|
||||||
|
- name: Install Java Development Kit 1.8
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
- name: Build APK & Run small tests
|
||||||
|
env:
|
||||||
|
RELEASE: 1
|
||||||
|
run: android/build.sh build
|
||||||
|
- name: Run medium tests
|
||||||
|
uses: ReactiveCircus/android-emulator-runner@v2.2.0
|
||||||
|
env:
|
||||||
|
RELEASE: 1
|
||||||
|
with:
|
||||||
|
api-level: 29
|
||||||
|
script: android/build.sh medium-tests
|
||||||
|
- name: Upload build to GitHub
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: Build
|
||||||
|
path: android/uhabits-android/build/outputs/
|
||||||
|
- name: Upload APK to Google Play
|
||||||
|
run: cd android && ./gradlew publishReleaseApk
|
||||||
26
.gitignore
vendored
@@ -1,22 +1,18 @@
|
|||||||
*.ap_
|
|
||||||
*.apk
|
|
||||||
*.class
|
|
||||||
*.dex
|
|
||||||
*.iml
|
*.iml
|
||||||
*.local.*
|
*.pbxuser
|
||||||
|
*.perspective
|
||||||
|
*.perspectivev3
|
||||||
*.swp
|
*.swp
|
||||||
*.trace
|
*~.nib
|
||||||
*~
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.classpath
|
._.DS_Store
|
||||||
|
.externalNativeBuild
|
||||||
.gradle
|
.gradle
|
||||||
.idea
|
.idea
|
||||||
.project
|
.secret
|
||||||
Thumbs.db
|
build
|
||||||
art/
|
|
||||||
bin/
|
|
||||||
build/
|
build/
|
||||||
captures/
|
captures
|
||||||
docs/
|
|
||||||
gen/
|
|
||||||
local.properties
|
local.properties
|
||||||
|
node_modules
|
||||||
|
*xcuserdata*
|
||||||
|
|||||||
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
82
CHANGELOG.md
@@ -1,5 +1,87 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
### 1.8.9 (Nov 18, 2020)
|
||||||
|
|
||||||
|
* Remove INTERNET permission
|
||||||
|
* Manage exceptions for when activities don't exist to handle intents (#181)
|
||||||
|
* MemoryHabitList: Inherit parent's order (#598)
|
||||||
|
* Remove notification groups; revert to default system behavior
|
||||||
|
* Remove SyncManager and Internet permission
|
||||||
|
|
||||||
|
### 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)
|
### 1.6.0 (Oct 10, 2016)
|
||||||
|
|
||||||
* Add option to make notifications sticky
|
* Add option to make notifications sticky
|
||||||
|
|||||||
107
README.md
@@ -1,22 +1,13 @@
|
|||||||
# Loop Habit Tracker
|
# Loop Habit Tracker
|
||||||
|
|
||||||
<a href="https://circleci.com/gh/iSoron/uhabits/tree/dev">
|
Loop is a mobile app that helps you create and maintain good habits,
|
||||||
<img src="https://img.shields.io/circleci/project/iSoron/uhabits/dev.svg">
|
|
||||||
</a>
|
|
||||||
<!--
|
|
||||||
<a href="https://codecov.io/github/iSoron/uhabits?branch=dev">
|
|
||||||
<img src="https://img.shields.io/codecov/c/github/iSoron/uhabits.svg" alt="Coverage via Codecov" />
|
|
||||||
</a>
|
|
||||||
-->
|
|
||||||
|
|
||||||
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
|
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
|
show you how your habits improved over time. It is completely ad-free and open
|
||||||
source.
|
source.
|
||||||
|
|
||||||
<p align="center">
|
<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="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="Git if on F-Droid" src="http://i.imgur.com/baSPE7X.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>
|
</p>
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
@@ -30,34 +21,32 @@ source.
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* **Simple, beautiful and modern interface.** Loop has a minimalistic interface
|
* <b>Beautiful, minimalistic and lightweight interface.</b>
|
||||||
that is easy to use and follows the material design guidelines.
|
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.
|
||||||
|
|
||||||
* **Habit score.** In addition to showing your current streak, Loop has an
|
* <b>Habit score.</b>
|
||||||
advanced algorithm for calculating the strength of your habits. Every
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
* **Detailed graphs and statistics.** Clearly see how your habits improved over
|
* <b>Flexible schedules.</b>
|
||||||
time with beautiful and detailed graphs. Scroll back to see the complete
|
In addition to daily habits, Loop supports habits with more complex schedules, such as 3 times per week or every other day.
|
||||||
history of your habits.
|
|
||||||
|
|
||||||
* **Flexible schedules.** Supports both daily habits and habits with more
|
* <b>Reminders.</b>
|
||||||
complex schedules, such as 3 times every week; one time every other week; or
|
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.
|
||||||
every other day.
|
|
||||||
|
|
||||||
* **Reminders.** Create an individual reminder for each habit, at a chosen hour
|
* <b>Widgets.</b>
|
||||||
of the day. Easily check, dismiss or snooze your habit directly from the
|
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.
|
||||||
notification, without opening the app.
|
|
||||||
|
|
||||||
* **Optimized for smartwatches.** Reminders can be checked, snoozed or
|
* <b>Take control of your data.</b>
|
||||||
dismissed directly from your Android Wear watch.
|
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.
|
||||||
|
|
||||||
* **Completely ad-free and open source.** There are absolutely no
|
* <b>No limitations.</b>
|
||||||
advertisements, annoying notifications or intrusive permissions in this app,
|
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.
|
||||||
and there will never be. The complete source code is available under the
|
|
||||||
GPLv3.
|
* <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
|
## Installing
|
||||||
|
|
||||||
@@ -84,7 +73,7 @@ contribute, even if you are not a software developer.
|
|||||||
|
|
||||||
* **Translate the app into your own language.** If you are not a native English
|
* **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,
|
speaker, and would like to see the app translated into your own language,
|
||||||
please join our [open translation project at POEditor][poedit]. If the translation
|
please join our [open translation project][poedit]. If the translation
|
||||||
is already completed, you are also very welcome to join and proofread it.
|
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
|
* **Write some code.** If you are an Android developer, you are very welcome to
|
||||||
@@ -94,34 +83,34 @@ contribute, even if you are not a software developer.
|
|||||||
|
|
||||||
<img align="right" src="https://www.gnu.org/graphics/gplv3-88x31.png">
|
<img align="right" src="https://www.gnu.org/graphics/gplv3-88x31.png">
|
||||||
|
|
||||||
Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
Copyright (C) 2016-2019 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
|
||||||
Loop Habit Tracker is free software: you can redistribute it and/or modify
|
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
|
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
|
Free Software Foundation, either version 3 of the License, or (at your
|
||||||
option) any later version.
|
option) any later version.
|
||||||
|
|
||||||
Loop Habit Tracker is distributed in the hope that it will be useful, but
|
Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
more details.
|
more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
You should have received a copy of the GNU General Public License along
|
||||||
with this program. If not, see <http://www.gnu.org/licenses/>.
|
with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
[screen1]: screenshots/original/uhabits1.png
|
[screen1]: screenshots/uhabits1.png
|
||||||
[screen2]: screenshots/original/uhabits2.png
|
[screen2]: screenshots/uhabits2.png
|
||||||
[screen3]: screenshots/original/uhabits3.png
|
[screen3]: screenshots/uhabits3.png
|
||||||
[screen4]: screenshots/original/uhabits4.png
|
[screen4]: screenshots/uhabits4.png
|
||||||
[screen5]: screenshots/original/uhabits5.png
|
[screen5]: screenshots/uhabits5.png
|
||||||
[screen6]: screenshots/original/uhabits6.png
|
[screen6]: screenshots/uhabits6.png
|
||||||
[screen1th]: screenshots/thumbs/uhabits1.png
|
[screen1th]: screenshots/uhabits1_th.png
|
||||||
[screen2th]: screenshots/thumbs/uhabits2.png
|
[screen2th]: screenshots/uhabits2_th.png
|
||||||
[screen3th]: screenshots/thumbs/uhabits3.png
|
[screen3th]: screenshots/uhabits3_th.png
|
||||||
[screen4th]: screenshots/thumbs/uhabits4.png
|
[screen4th]: screenshots/uhabits4_th.png
|
||||||
[screen5th]: screenshots/thumbs/uhabits5.png
|
[screen5th]: screenshots/uhabits5_th.png
|
||||||
[screen6th]: screenshots/thumbs/uhabits6.png
|
[screen6th]: screenshots/uhabits6_th.png
|
||||||
[poedit]: https://poeditor.com/join/project/8DWX5pfjS0
|
[poedit]: http://translate.loophabits.org
|
||||||
[playstore]: https://play.google.com/store/apps/details?id=org.isoron.uhabits
|
[playstore]: https://play.google.com/store/apps/details?id=org.isoron.uhabits
|
||||||
[releases]: https://github.com/iSoron/uhabits/releases
|
[releases]: https://github.com/iSoron/uhabits/releases
|
||||||
[fdroid]: http://f-droid.org/app/org.isoron.uhabits
|
[fdroid]: http://f-droid.org/app/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"/>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
*
|
*
|
||||||
* This file is part of Loop Habit Tracker.
|
* This file is part of Loop Habit Tracker.
|
||||||
*
|
*
|
||||||
@@ -17,75 +17,33 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.activities;
|
package org.isoron.androidbase;
|
||||||
|
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.os.*;
|
import android.os.*;
|
||||||
import android.support.annotation.*;
|
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import androidx.annotation.NonNull;
|
||||||
import org.isoron.uhabits.utils.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.Process;
|
import java.text.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.inject.*;
|
import javax.inject.*;
|
||||||
|
|
||||||
/**
|
public class AndroidBugReporter
|
||||||
* Base class for all systems class in the application.
|
|
||||||
* <p>
|
|
||||||
* Classes derived from BaseSystem are responsible for handling events and
|
|
||||||
* sending requests to the Android operating system. Examples include capturing
|
|
||||||
* a bug report, obtaining device information, or requesting runtime
|
|
||||||
* permissions.
|
|
||||||
*/
|
|
||||||
@ActivityScope
|
|
||||||
public class BaseSystem
|
|
||||||
{
|
{
|
||||||
private Context context;
|
private final Context context;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public BaseSystem(@ActivityContext Context context)
|
public AndroidBugReporter(@NonNull @AppContext Context context)
|
||||||
{
|
{
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Captures a bug report and saves it to a file in the SD card.
|
* Captures and returns a bug report. The bug report contains some device
|
||||||
* <p>
|
* information and the logcat.
|
||||||
* 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 File dumpBugReportToFile() throws IOException
|
|
||||||
{
|
|
||||||
String date =
|
|
||||||
DateFormats.getBackupDateFormat().format(DateUtils.getLocalTime());
|
|
||||||
|
|
||||||
if (context == null) throw new RuntimeException(
|
|
||||||
"application context should not be null");
|
|
||||||
File dir = FileUtils.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();
|
|
||||||
|
|
||||||
return logFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Captures and returns a bug report.
|
|
||||||
* <p>
|
|
||||||
* The bug report contains some device information and the logcat.
|
|
||||||
*
|
*
|
||||||
* @return a String containing the bug report.
|
* @return a String containing the bug report.
|
||||||
* @throws IOException when any I/O error occur.
|
* @throws IOException when any I/O error occur.
|
||||||
@@ -103,36 +61,7 @@ public class BaseSystem
|
|||||||
return log;
|
return log;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLogcat() throws IOException
|
public String getDeviceInfo()
|
||||||
{
|
|
||||||
int maxLineCount = 250;
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
String[] command = new String[]{ "logcat", "-d" };
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getDeviceInfo()
|
|
||||||
{
|
{
|
||||||
if (context == null) return "null context\n";
|
if (context == null) return "null context\n";
|
||||||
|
|
||||||
@@ -157,4 +86,71 @@ public class BaseSystem
|
|||||||
String.format("External storage state: %s\n\n",
|
String.format("External storage state: %s\n\n",
|
||||||
Environment.getExternalStorageState());
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.androidbase;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
@@ -17,18 +17,18 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.androidbase;
|
||||||
|
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
|
|
||||||
import dagger.*;
|
import dagger.*;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class AppModule
|
public class AppContextModule
|
||||||
{
|
{
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
public AppModule(@AppContext Context context)
|
public AppContextModule(@AppContext Context context)
|
||||||
{
|
{
|
||||||
this.context = context;
|
this.context = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.activities;
|
package org.isoron.androidbase.activities;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
@@ -17,20 +17,26 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.androidbase.activities;
|
||||||
|
|
||||||
|
import android.content.*;
|
||||||
import org.isoron.uhabits.models.sqlite.*;
|
|
||||||
import org.isoron.uhabits.tasks.*;
|
|
||||||
|
|
||||||
import dagger.*;
|
import dagger.*;
|
||||||
|
|
||||||
@AppScope
|
@Module
|
||||||
@Component(modules = {
|
public class ActivityContextModule
|
||||||
AppModule.class, SingleThreadTaskRunner.class, SQLModelFactory.class
|
|
||||||
})
|
|
||||||
public interface AndroidTestComponent extends AppComponent
|
|
||||||
{
|
{
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public ActivityContextModule(Context context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@ActivityContext
|
||||||
|
public Context getContext()
|
||||||
|
{
|
||||||
|
return context;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.activities;
|
package org.isoron.androidbase.activities;
|
||||||
|
|
||||||
import javax.inject.*;
|
import javax.inject.*;
|
||||||
|
|
||||||
@@ -17,20 +17,19 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.activities;
|
package org.isoron.androidbase.activities;
|
||||||
|
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.os.*;
|
import android.os.*;
|
||||||
import android.support.annotation.*;
|
|
||||||
import android.support.v7.app.*;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.*;
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.androidbase.*;
|
||||||
import org.isoron.uhabits.activities.habits.list.*;
|
|
||||||
import org.isoron.uhabits.models.*;
|
|
||||||
import org.isoron.uhabits.models.sqlite.*;
|
|
||||||
|
|
||||||
import static android.R.anim.*;
|
import static android.R.anim.fade_in;
|
||||||
|
import static android.R.anim.fade_out;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all activities in the application.
|
* Base class for all activities in the application.
|
||||||
@@ -41,28 +40,19 @@ import static android.R.anim.*;
|
|||||||
* {@link BaseScreen}.
|
* {@link BaseScreen}.
|
||||||
* <p>
|
* <p>
|
||||||
* A BaseActivity also installs an {@link java.lang.Thread.UncaughtExceptionHandler}
|
* A BaseActivity also installs an {@link java.lang.Thread.UncaughtExceptionHandler}
|
||||||
* to the main thread that logs the exception to the disk before the application
|
* to the main thread. By default, this handler is an instance of
|
||||||
* crashes.
|
* 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
|
abstract public class BaseActivity extends AppCompatActivity
|
||||||
implements Thread.UncaughtExceptionHandler
|
|
||||||
{
|
{
|
||||||
@Nullable
|
@Nullable
|
||||||
private BaseMenu baseMenu;
|
private BaseMenu baseMenu;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Thread.UncaughtExceptionHandler androidExceptionHandler;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private BaseScreen screen;
|
private BaseScreen screen;
|
||||||
|
|
||||||
private ActivityComponent component;
|
|
||||||
|
|
||||||
public ActivityComponent getComponent()
|
|
||||||
{
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(@Nullable Menu menu)
|
public boolean onCreateOptionsMenu(@Nullable Menu menu)
|
||||||
{
|
{
|
||||||
@@ -80,13 +70,13 @@ abstract public class BaseActivity extends AppCompatActivity
|
|||||||
return baseMenu.onItemSelected(item);
|
return baseMenu.onItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void restartWithFade()
|
public void restartWithFade(Class<?> cls)
|
||||||
{
|
{
|
||||||
new Handler().postDelayed(() -> {
|
new Handler().postDelayed(() ->
|
||||||
Intent intent = new Intent(this, ListHabitsActivity.class);
|
{
|
||||||
finish();
|
finish();
|
||||||
overridePendingTransition(fade_in, fade_out);
|
overridePendingTransition(fade_in, fade_out);
|
||||||
startActivity(intent);
|
startActivity(new Intent(this, cls));
|
||||||
|
|
||||||
}, 500); // HACK: Let the menu disappear first
|
}, 500); // HACK: Let the menu disappear first
|
||||||
}
|
}
|
||||||
@@ -111,35 +101,6 @@ abstract public class BaseActivity extends AppCompatActivity
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void uncaughtException(@Nullable Thread thread,
|
|
||||||
@Nullable Throwable ex)
|
|
||||||
{
|
|
||||||
if (ex == null) return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ex.printStackTrace();
|
|
||||||
new BaseSystem(this).dumpBugReportToFile();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ex.getCause() instanceof InconsistentDatabaseException)
|
|
||||||
{
|
|
||||||
HabitsApplication app = (HabitsApplication) getApplication();
|
|
||||||
HabitList habits = app.getComponent().getHabitList();
|
|
||||||
habits.repair();
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (androidExceptionHandler != null)
|
|
||||||
androidExceptionHandler.uncaughtException(thread, ex);
|
|
||||||
else System.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int request, int result, Intent data)
|
protected void onActivityResult(int request, int result, Intent data)
|
||||||
{
|
{
|
||||||
@@ -151,18 +112,32 @@ abstract public class BaseActivity extends AppCompatActivity
|
|||||||
protected void onCreate(Bundle savedInstanceState)
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(getExceptionHandler());
|
||||||
|
}
|
||||||
|
|
||||||
androidExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
protected Thread.UncaughtExceptionHandler getExceptionHandler()
|
||||||
Thread.setDefaultUncaughtExceptionHandler(this);
|
{
|
||||||
|
return new BaseExceptionHandler(this);
|
||||||
|
}
|
||||||
|
|
||||||
HabitsApplication app = (HabitsApplication) getApplicationContext();
|
@Override
|
||||||
|
protected void onResume()
|
||||||
|
{
|
||||||
|
super.onResume();
|
||||||
|
if(screen != null) screen.reattachDialogs();
|
||||||
|
}
|
||||||
|
|
||||||
component = DaggerActivityComponent
|
@Override
|
||||||
.builder()
|
public void startActivity(Intent intent)
|
||||||
.activityModule(new ActivityModule(this))
|
{
|
||||||
.appComponent(app.getComponent())
|
try
|
||||||
.build();
|
{
|
||||||
|
super.startActivity(intent);
|
||||||
component.getThemeSwitcher().apply();
|
}
|
||||||
|
catch (ActivityNotFoundException e)
|
||||||
|
{
|
||||||
|
if (this.screen != null)
|
||||||
|
this.screen.showMessage(R.string.activity_not_found);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,31 +17,22 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.activities;
|
package org.isoron.androidbase.activities;
|
||||||
|
|
||||||
import android.content.*;
|
|
||||||
|
|
||||||
import dagger.*;
|
import dagger.*;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class ActivityModule
|
public class BaseActivityModule
|
||||||
{
|
{
|
||||||
private BaseActivity activity;
|
private BaseActivity activity;
|
||||||
|
|
||||||
public ActivityModule(BaseActivity activity)
|
public BaseActivityModule(BaseActivity activity)
|
||||||
{
|
{
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
public BaseActivity getActivity()
|
public BaseActivity getBaseActivity()
|
||||||
{
|
|
||||||
return activity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@ActivityContext
|
|
||||||
public Context getContext()
|
|
||||||
{
|
{
|
||||||
return activity;
|
return activity;
|
||||||
}
|
}
|
||||||
@@ -17,12 +17,12 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.activities;
|
package org.isoron.androidbase.activities;
|
||||||
|
|
||||||
import android.support.annotation.*;
|
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
|
|
||||||
import javax.annotation.*;
|
import androidx.annotation.MenuRes;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all the menus in the application.
|
* Base class for all the menus in the application.
|
||||||
@@ -41,6 +41,12 @@ public abstract class BaseMenu
|
|||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public BaseActivity getActivity()
|
||||||
|
{
|
||||||
|
return activity;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declare that the menu has changed, and should be recreated.
|
* Declare that the menu has changed, and should be recreated.
|
||||||
*/
|
*/
|
||||||
@@ -64,14 +70,13 @@ public abstract class BaseMenu
|
|||||||
/**
|
/**
|
||||||
* Called when the menu is first displayed.
|
* Called when the menu is first displayed.
|
||||||
* <p>
|
* <p>
|
||||||
* This method cannot be overridden. The application should override the
|
* This method should not be overridden. The application should override
|
||||||
* methods onCreate(Menu) and getMenuResourceId instead.
|
* the methods onCreate(Menu) and getMenuResourceId instead.
|
||||||
*
|
*
|
||||||
* @param inflater a menu inflater, for creating the menu
|
* @param inflater a menu inflater, for creating the menu
|
||||||
* @param menu the menu that is being created.
|
* @param menu the menu that is being created.
|
||||||
*/
|
*/
|
||||||
public final void onCreate(@NonNull MenuInflater inflater,
|
public void onCreate(@NonNull MenuInflater inflater, @NonNull Menu menu)
|
||||||
@NonNull Menu menu)
|
|
||||||
{
|
{
|
||||||
menu.clear();
|
menu.clear();
|
||||||
inflater.inflate(getMenuResourceId(), menu);
|
inflater.inflate(getMenuResourceId(), menu);
|
||||||
@@ -94,6 +99,6 @@ public abstract class BaseMenu
|
|||||||
*
|
*
|
||||||
* @return id of the menu resource.
|
* @return id of the menu resource.
|
||||||
*/
|
*/
|
||||||
@Resource
|
@MenuRes
|
||||||
protected abstract int getMenuResourceId();
|
protected abstract int getMenuResourceId();
|
||||||
}
|
}
|
||||||
@@ -17,20 +17,21 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.activities;
|
package org.isoron.androidbase.activities;
|
||||||
|
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.support.annotation.*;
|
|
||||||
import android.support.v4.content.res.*;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.v7.widget.Toolbar;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
import android.widget.*;
|
import android.widget.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.androidbase.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.androidbase.utils.*;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.*;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.os.Build.VERSION_CODES.*;
|
import static android.os.Build.VERSION_CODES.LOLLIPOP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all root views in the application.
|
* Base class for all root views in the application.
|
||||||
@@ -42,36 +43,42 @@ import static android.os.Build.VERSION_CODES.*;
|
|||||||
*/
|
*/
|
||||||
public abstract class BaseRootView extends FrameLayout
|
public abstract class BaseRootView extends FrameLayout
|
||||||
{
|
{
|
||||||
|
@NonNull
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
private final BaseActivity activity;
|
protected boolean shouldDisplayHomeAsUp = false;
|
||||||
|
|
||||||
private final ThemeSwitcher themeSwitcher;
|
@Nullable
|
||||||
|
private BaseScreen screen;
|
||||||
|
|
||||||
public BaseRootView(Context context)
|
public BaseRootView(@NonNull Context context)
|
||||||
{
|
{
|
||||||
super(context);
|
super(context);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
activity = (BaseActivity) context;
|
|
||||||
themeSwitcher = activity.getComponent().getThemeSwitcher();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getDisplayHomeAsUp()
|
public boolean getDisplayHomeAsUp()
|
||||||
{
|
{
|
||||||
return false;
|
return shouldDisplayHomeAsUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisplayHomeAsUp(boolean b)
|
||||||
|
{
|
||||||
|
shouldDisplayHomeAsUp = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public abstract Toolbar getToolbar();
|
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()
|
public int getToolbarColor()
|
||||||
{
|
{
|
||||||
if (SDK_INT < LOLLIPOP && !themeSwitcher.isNightMode())
|
|
||||||
{
|
|
||||||
return ResourcesCompat.getColor(context.getResources(),
|
|
||||||
R.color.grey_900, context.getTheme());
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledResources res = new StyledResources(context);
|
StyledResources res = new StyledResources(context);
|
||||||
return res.getColor(R.attr.colorPrimary);
|
return res.getColor(R.attr.colorPrimary);
|
||||||
}
|
}
|
||||||
@@ -86,7 +93,18 @@ public abstract class BaseRootView extends FrameLayout
|
|||||||
if (view != null) view.setVisibility(GONE);
|
if (view != null) view.setVisibility(GONE);
|
||||||
|
|
||||||
view = findViewById(R.id.headerShadow);
|
view = findViewById(R.id.headerShadow);
|
||||||
if(view != null) view.setVisibility(GONE);
|
if (view != null) view.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onAttachedToScreen(BaseScreen screen)
|
||||||
|
{
|
||||||
|
this.screen = screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public BaseScreen getScreen()
|
||||||
|
{
|
||||||
|
return screen;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,29 +17,34 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.activities;
|
package org.isoron.androidbase.activities;
|
||||||
|
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.graphics.*;
|
import android.graphics.*;
|
||||||
import android.graphics.drawable.*;
|
import android.graphics.drawable.*;
|
||||||
import android.net.*;
|
import android.net.*;
|
||||||
import android.os.*;
|
import android.os.*;
|
||||||
import android.support.annotation.*;
|
|
||||||
import android.support.design.widget.*;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.v4.content.res.*;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v7.app.*;
|
import androidx.annotation.StringRes;
|
||||||
import android.support.v7.view.ActionMode;
|
import androidx.core.content.res.*;
|
||||||
import android.support.v7.widget.Toolbar;
|
import androidx.appcompat.app.*;
|
||||||
|
import androidx.appcompat.view.ActionMode;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
import android.widget.*;
|
import android.widget.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import org.isoron.uhabits.utils.*;
|
|
||||||
|
import org.isoron.androidbase.*;
|
||||||
|
import org.isoron.androidbase.utils.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.*;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.os.Build.VERSION_CODES.*;
|
import static android.os.Build.VERSION_CODES.LOLLIPOP;
|
||||||
|
import static androidx.core.content.FileProvider.getUriForFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all screens in the application.
|
* Base class for all screens in the application.
|
||||||
@@ -58,13 +63,28 @@ public class BaseScreen
|
|||||||
@Nullable
|
@Nullable
|
||||||
private BaseSelectionMenu selectionMenu;
|
private BaseSelectionMenu selectionMenu;
|
||||||
|
|
||||||
private Snackbar snackbar;
|
protected Snackbar snackbar;
|
||||||
|
|
||||||
public BaseScreen(@NonNull BaseActivity activity)
|
public BaseScreen(@NonNull BaseActivity activity)
|
||||||
{
|
{
|
||||||
this.activity = 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
|
@Deprecated
|
||||||
public static void setupActionBarColor(@NonNull AppCompatActivity activity,
|
public static void setupActionBarColor(@NonNull AppCompatActivity activity,
|
||||||
int color)
|
int color)
|
||||||
@@ -80,7 +100,6 @@ public class BaseScreen
|
|||||||
|
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
|
||||||
ColorDrawable drawable = new ColorDrawable(color);
|
ColorDrawable drawable = new ColorDrawable(color);
|
||||||
actionBar.setBackgroundDrawable(drawable);
|
actionBar.setBackgroundDrawable(drawable);
|
||||||
|
|
||||||
@@ -99,21 +118,6 @@ public class BaseScreen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the screen that its contents should be updated.
|
* Notifies the screen that its contents should be updated.
|
||||||
*/
|
*/
|
||||||
@@ -127,7 +131,8 @@ public class BaseScreen
|
|||||||
{
|
{
|
||||||
if (rootView == null) return;
|
if (rootView == null) return;
|
||||||
|
|
||||||
activity.runOnUiThread(() -> {
|
activity.runOnUiThread(() ->
|
||||||
|
{
|
||||||
Toolbar toolbar = rootView.getToolbar();
|
Toolbar toolbar = rootView.getToolbar();
|
||||||
activity.setSupportActionBar(toolbar);
|
activity.setSupportActionBar(toolbar);
|
||||||
ActionBar actionBar = activity.getSupportActionBar();
|
ActionBar actionBar = activity.getSupportActionBar();
|
||||||
@@ -156,6 +161,15 @@ public class BaseScreen
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Sets the menu to be shown by this screen.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -179,7 +193,7 @@ public class BaseScreen
|
|||||||
this.rootView = rootView;
|
this.rootView = rootView;
|
||||||
activity.setContentView(rootView);
|
activity.setContentView(rootView);
|
||||||
if (rootView == null) return;
|
if (rootView == null) return;
|
||||||
|
rootView.onAttachedToScreen(this);
|
||||||
invalidateToolbar();
|
invalidateToolbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +218,7 @@ public class BaseScreen
|
|||||||
if (snackbar == null)
|
if (snackbar == null)
|
||||||
{
|
{
|
||||||
snackbar = Snackbar.make(rootView, stringId, Snackbar.LENGTH_SHORT);
|
snackbar = Snackbar.make(rootView, stringId, Snackbar.LENGTH_SHORT);
|
||||||
int tvId = android.support.design.R.id.snackbar_text;
|
int tvId = R.id.snackbar_text;
|
||||||
TextView tv = (TextView) snackbar.getView().findViewById(tvId);
|
TextView tv = (TextView) snackbar.getView().findViewById(tvId);
|
||||||
tv.setTextColor(Color.WHITE);
|
tv.setTextColor(Color.WHITE);
|
||||||
}
|
}
|
||||||
@@ -230,11 +244,14 @@ public class BaseScreen
|
|||||||
|
|
||||||
public void showSendFileScreen(@NonNull String archiveFilename)
|
public void showSendFileScreen(@NonNull String archiveFilename)
|
||||||
{
|
{
|
||||||
|
File file = new File(archiveFilename);
|
||||||
|
Uri fileUri = getUriForFile(activity, "org.isoron.uhabits", file);
|
||||||
|
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(Intent.ACTION_SEND);
|
intent.setAction(Intent.ACTION_SEND);
|
||||||
intent.setType("application/zip");
|
intent.setType("application/zip");
|
||||||
intent.putExtra(Intent.EXTRA_STREAM,
|
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
||||||
Uri.fromFile(new File(archiveFilename)));
|
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,10 +17,11 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.activities;
|
package org.isoron.androidbase.activities;
|
||||||
|
|
||||||
import android.support.annotation.*;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.v7.view.ActionMode;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.view.ActionMode;
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,16 +60,16 @@ public abstract class BaseSelectionMenu
|
|||||||
/**
|
/**
|
||||||
* Called when the menu is first displayed.
|
* Called when the menu is first displayed.
|
||||||
* <p>
|
* <p>
|
||||||
* This method cannot be overridden. The application should override the
|
* This method should not be overridden. The application should override
|
||||||
* methods onCreate(Menu) and getMenuResourceId instead.
|
* the methods onCreate(Menu) and getMenuResourceId instead.
|
||||||
*
|
*
|
||||||
* @param inflater a menu inflater, for creating the menu
|
* @param inflater a menu inflater, for creating the menu
|
||||||
* @param mode the action mode associated with this menu.
|
* @param mode the action mode associated with this menu.
|
||||||
* @param menu the menu that is being created.
|
* @param menu the menu that is being created.
|
||||||
*/
|
*/
|
||||||
public final void onCreate(@NonNull MenuInflater inflater,
|
public void onCreate(@NonNull MenuInflater inflater,
|
||||||
@NonNull ActionMode mode,
|
@NonNull ActionMode mode,
|
||||||
@NonNull Menu menu)
|
@NonNull Menu menu)
|
||||||
{
|
{
|
||||||
this.actionMode = mode;
|
this.actionMode = mode;
|
||||||
inflater.inflate(getResourceId(), menu);
|
inflater.inflate(getResourceId(), menu);
|
||||||
@@ -17,80 +17,12 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.utils;
|
package org.isoron.androidbase.utils;
|
||||||
|
|
||||||
import android.content.*;
|
|
||||||
import android.graphics.*;
|
import android.graphics.*;
|
||||||
import android.util.*;
|
|
||||||
|
|
||||||
public abstract class ColorUtils
|
public abstract class ColorUtils
|
||||||
{
|
{
|
||||||
public static String CSV_PALETTE[] = {
|
|
||||||
"#D32F2F", // 0 red
|
|
||||||
"#E64A19", // 1 orange
|
|
||||||
"#F9A825", // 2 yellow
|
|
||||||
"#AFB42B", // 3 light green
|
|
||||||
"#388E3C", // 4 dark green
|
|
||||||
"#00897B", // 5 teal
|
|
||||||
"#00ACC1", // 6 cyan
|
|
||||||
"#039BE5", // 7 blue
|
|
||||||
"#5E35B1", // 8 deep purple
|
|
||||||
"#8E24AA", // 9 purple
|
|
||||||
"#D81B60", // 10 pink
|
|
||||||
"#303030", // 11 dark grey
|
|
||||||
"#aaaaaa" // 12 light grey
|
|
||||||
};
|
|
||||||
|
|
||||||
public static int colorToPaletteIndex(Context context, int color)
|
|
||||||
{
|
|
||||||
StyledResources res = new StyledResources(context);
|
|
||||||
int[] palette = res.getPalette();
|
|
||||||
|
|
||||||
for (int k = 0; k < palette.length; k++)
|
|
||||||
if (palette[k] == color) return k;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getAndroidTestColor(int index)
|
|
||||||
{
|
|
||||||
int palette[] = {
|
|
||||||
Color.parseColor("#D32F2F"), // 0 red
|
|
||||||
Color.parseColor("#E64A19"), // 1 orange
|
|
||||||
Color.parseColor("#F9A825"), // 2 yellow
|
|
||||||
Color.parseColor("#AFB42B"), // 3 light green
|
|
||||||
Color.parseColor("#388E3C"), // 4 dark green
|
|
||||||
Color.parseColor("#00897B"), // 5 teal
|
|
||||||
Color.parseColor("#00ACC1"), // 6 cyan
|
|
||||||
Color.parseColor("#039BE5"), // 7 blue
|
|
||||||
Color.parseColor("#5E35B1"), // 8 deep purple
|
|
||||||
Color.parseColor("#8E24AA"), // 9 purple
|
|
||||||
Color.parseColor("#D81B60"), // 10 pink
|
|
||||||
Color.parseColor("#303030"), // 11 dark grey
|
|
||||||
Color.parseColor("#aaaaaa") // 12 light grey
|
|
||||||
};
|
|
||||||
|
|
||||||
return palette[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getColor(Context context, int paletteColor)
|
|
||||||
{
|
|
||||||
if (context == null)
|
|
||||||
throw new IllegalArgumentException("Context is null");
|
|
||||||
|
|
||||||
StyledResources res = new StyledResources(context);
|
|
||||||
int palette[] = res.getPalette();
|
|
||||||
if (paletteColor < 0 || paletteColor >= palette.length)
|
|
||||||
{
|
|
||||||
Log.w("ColorHelper",
|
|
||||||
String.format("Invalid color: %d. Returning default.",
|
|
||||||
paletteColor));
|
|
||||||
paletteColor = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return palette[paletteColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int mixColors(int color1, int color2, float amount)
|
public static int mixColors(int color1, int color2, float amount)
|
||||||
{
|
{
|
||||||
final byte ALPHA_CHANNEL = 24;
|
final byte ALPHA_CHANNEL = 24;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
*
|
*
|
||||||
* This file is part of Loop Habit Tracker.
|
* This file is part of Loop Habit Tracker.
|
||||||
*
|
*
|
||||||
@@ -17,15 +17,13 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.utils;
|
package org.isoron.androidbase.utils;
|
||||||
|
|
||||||
import android.content.*;
|
|
||||||
import android.os.*;
|
import android.os.*;
|
||||||
import android.support.annotation.*;
|
|
||||||
import android.support.v4.content.*;
|
|
||||||
import android.util.*;
|
import android.util.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
@@ -54,8 +52,8 @@ public abstract class FileUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static File getDir(@NonNull File potentialParentDirs[],
|
public static File getDir(@NonNull File potentialParentDirs[],
|
||||||
@Nullable String relativePath)
|
@Nullable String relativePath)
|
||||||
{
|
{
|
||||||
if (relativePath == null) relativePath = "";
|
if (relativePath == null) relativePath = "";
|
||||||
|
|
||||||
@@ -69,7 +67,7 @@ public abstract class FileUtils
|
|||||||
|
|
||||||
if (chosenDir == null)
|
if (chosenDir == null)
|
||||||
{
|
{
|
||||||
Log.e("DatabaseHelper",
|
Log.e("FileUtils",
|
||||||
"getDir: all potential parents are null or non-writable");
|
"getDir: all potential parents are null or non-writable");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -78,7 +76,7 @@ public abstract class FileUtils
|
|||||||
String.format("%s/%s/", chosenDir.getAbsolutePath(), relativePath));
|
String.format("%s/%s/", chosenDir.getAbsolutePath(), relativePath));
|
||||||
if (!dir.exists() && !dir.mkdirs())
|
if (!dir.exists() && !dir.mkdirs())
|
||||||
{
|
{
|
||||||
Log.e("DatabaseHelper",
|
Log.e("FileUtils",
|
||||||
"getDir: chosen dir does not exist and cannot be created");
|
"getDir: chosen dir does not exist and cannot be created");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -86,23 +84,6 @@ public abstract class FileUtils
|
|||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static File getFilesDir(@Nullable String relativePath)
|
|
||||||
{
|
|
||||||
Context context = HabitsApplication.getContext();
|
|
||||||
File externalFilesDirs[] =
|
|
||||||
ContextCompat.getExternalFilesDirs(context, null);
|
|
||||||
|
|
||||||
if (externalFilesDirs == null)
|
|
||||||
{
|
|
||||||
Log.e("DatabaseHelper",
|
|
||||||
"getFilesDir: getExternalFilesDirs returned null");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getDir(externalFilesDirs, relativePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static File getSDCardDir(@Nullable String relativePath)
|
public static File getSDCardDir(@Nullable String 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,14 +17,16 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.utils;
|
package org.isoron.androidbase.utils;
|
||||||
|
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.content.res.*;
|
import android.content.res.*;
|
||||||
import android.graphics.drawable.*;
|
import android.graphics.drawable.*;
|
||||||
import android.support.annotation.*;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import androidx.annotation.AttrRes;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.isoron.androidbase.*;
|
||||||
|
|
||||||
public class StyledResources
|
public class StyledResources
|
||||||
{
|
{
|
||||||
@@ -51,6 +53,15 @@ public class StyledResources
|
|||||||
return bool;
|
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)
|
public int getColor(@AttrRes int attrId)
|
||||||
{
|
{
|
||||||
TypedArray ta = getTypedArray(attrId);
|
TypedArray ta = getTypedArray(attrId);
|
||||||
@@ -80,13 +91,13 @@ public class StyledResources
|
|||||||
|
|
||||||
public int[] getPalette()
|
public int[] getPalette()
|
||||||
{
|
{
|
||||||
int resourceId = getStyleResource(R.attr.palette);
|
int resourceId = getResource(R.attr.palette);
|
||||||
if (resourceId < 0) throw new RuntimeException("resource not found");
|
if (resourceId < 0) throw new RuntimeException("resource not found");
|
||||||
|
|
||||||
return context.getResources().getIntArray(resourceId);
|
return context.getResources().getIntArray(resourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getStyleResource(@AttrRes int attrId)
|
public int getResource(@AttrRes int attrId)
|
||||||
{
|
{
|
||||||
TypedArray ta = getTypedArray(attrId);
|
TypedArray ta = getTypedArray(attrId);
|
||||||
int resourceId = ta.getResourceId(0, -1);
|
int resourceId = ta.getResourceId(0, -1);
|
||||||
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>
|
||||||
@@ -1,97 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
|
||||||
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.
|
|
||||||
-->
|
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<array name="lightPalette">
|
|
||||||
<item>@color/red_700</item>
|
|
||||||
<item>@color/deep_orange_700</item>
|
|
||||||
<item>@color/yellow_800</item>
|
|
||||||
<item>@color/lime_700</item>
|
|
||||||
<item>@color/green_700</item>
|
|
||||||
<item>@color/teal_600</item>
|
|
||||||
<item>@color/cyan_600</item>
|
|
||||||
<item>@color/light_blue_600</item>
|
|
||||||
<item>@color/deep_purple_600</item>
|
|
||||||
<item>@color/purple_600</item>
|
|
||||||
<item>@color/pink_600</item>
|
|
||||||
<item>@color/grey_800</item>
|
|
||||||
<item>@color/grey_500</item>
|
|
||||||
</array>
|
|
||||||
|
|
||||||
<array name="darkPalette">
|
|
||||||
<item>@color/red_200</item>
|
|
||||||
<item>@color/deep_orange_200</item>
|
|
||||||
<item>@color/yellow_200</item>
|
|
||||||
<item>@color/lime_200</item>
|
|
||||||
<item>@color/green_A200</item>
|
|
||||||
<item>@color/teal_200</item>
|
|
||||||
<item>@color/cyan_200</item>
|
|
||||||
<item>@color/light_blue_200</item>
|
|
||||||
<item>@color/deep_purple_200</item>
|
|
||||||
<item>@color/purple_200</item>
|
|
||||||
<item>@color/pink_200</item>
|
|
||||||
<item>@color/grey_100</item>
|
|
||||||
<item>@color/grey_500</item>
|
|
||||||
</array>
|
|
||||||
|
|
||||||
<array name="transparentWidgetPalette">
|
|
||||||
<item>@color/red_800</item>
|
|
||||||
<item>@color/deep_orange_800</item>
|
|
||||||
<item>@color/yellow_800</item>
|
|
||||||
<item>@color/lime_800</item>
|
|
||||||
<item>@color/green_700</item>
|
|
||||||
<item>@color/teal_700</item>
|
|
||||||
<item>@color/cyan_700</item>
|
|
||||||
<item>@color/light_blue_700</item>
|
|
||||||
<item>@color/deep_purple_700</item>
|
|
||||||
<item>@color/purple_700</item>
|
|
||||||
<item>@color/pink_700</item>
|
|
||||||
<item>@color/black_aa</item>
|
|
||||||
<item>@color/black_aa</item>
|
|
||||||
</array>
|
|
||||||
|
|
||||||
<!-- 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>
|
|
||||||
|
|
||||||
<!-- 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>
|
|
||||||
|
|
||||||
<!-- Material design color palette -->
|
|
||||||
<color name="red_50">#FFEBEE</color>
|
<color name="red_50">#FFEBEE</color>
|
||||||
<color name="red_100">#FFCDD2</color>
|
<color name="red_100">#FFCDD2</color>
|
||||||
<color name="red_200">#EF9A9A</color>
|
<color name="red_200">#EF9A9A</color>
|
||||||
23
android/android-base/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016-2020 Á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>
|
||||||
|
<string name="activity_not_found">No app was found to support this action</string>
|
||||||
|
</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,18 +16,15 @@
|
|||||||
|
|
||||||
package com.android.colorpicker;
|
package com.android.colorpicker;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.*;
|
||||||
import android.app.Dialog;
|
import android.os.*;
|
||||||
import android.os.Bundle;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import android.support.v7.app.AlertDialog;
|
import androidx.appcompat.app.*;
|
||||||
import android.support.v7.app.AppCompatDialogFragment;
|
import android.view.*;
|
||||||
import android.view.LayoutInflater;
|
import android.widget.*;
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
|
|
||||||
import com.android.colorpicker.ColorPickerSwatch.OnColorSelectedListener;
|
import com.android.*;
|
||||||
|
import com.android.colorpicker.ColorPickerSwatch.*;
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dialog which takes in as input an array of palette and creates a palette allowing the user to
|
* A dialog which takes in as input an array of palette and creates a palette allowing the user to
|
||||||
@@ -16,18 +16,14 @@
|
|||||||
|
|
||||||
package com.android.colorpicker;
|
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 com.android.*;
|
||||||
import android.content.res.Resources;
|
import com.android.colorpicker.ColorPickerSwatch.*;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A color picker custom view which creates an grid of color squares. The number of squares per
|
* 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;
|
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 com.android.*;
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a circular swatch of a specified color. Adds a checkmark if marked as checked.
|
* Creates a circular swatch of a specified color. Adds a checkmark if marked as checked.
|
||||||
@@ -16,36 +16,23 @@
|
|||||||
|
|
||||||
package com.android.datetimepicker.date;
|
package com.android.datetimepicker.date;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import android.animation.*;
|
||||||
import java.util.Calendar;
|
import android.app.*;
|
||||||
import java.util.HashSet;
|
import android.content.res.*;
|
||||||
import java.util.Iterator;
|
import android.os.*;
|
||||||
import java.util.Locale;
|
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 java.text.*;
|
||||||
import android.app.Activity;
|
import java.util.*;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog allowing users to select a date.
|
* Dialog allowing users to select a date.
|
||||||
@@ -16,37 +16,27 @@
|
|||||||
|
|
||||||
package com.android.datetimepicker.date;
|
package com.android.datetimepicker.date;
|
||||||
|
|
||||||
import java.security.InvalidParameterException;
|
import android.content.*;
|
||||||
import java.util.Calendar;
|
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.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
|
* A calendar-like view displaying a specified month and the appropriate selectable day numbers
|
||||||
@@ -16,16 +16,14 @@
|
|||||||
|
|
||||||
package com.android.datetimepicker.date;
|
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 com.android.*;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A text view which, when pressed or activated, displays a blue circle around the text.
|
* A text view which, when pressed or activated, displays a blue circle around the text.
|
||||||
@@ -16,24 +16,18 @@
|
|||||||
|
|
||||||
package com.android.datetimepicker.date;
|
package com.android.datetimepicker.date;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import android.content.*;
|
||||||
import java.util.List;
|
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 java.util.*;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a selectable list of years.
|
* Displays a selectable list of years.
|
||||||
@@ -16,20 +16,17 @@
|
|||||||
|
|
||||||
package com.android.datetimepicker.time;
|
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 java.text.*;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw the two smaller AM and PM circles next to where the larger circle will be.
|
* 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;
|
package com.android.datetimepicker.time;
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.*;
|
||||||
import android.content.res.Resources;
|
import android.content.res.*;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.*;
|
||||||
import android.graphics.Paint;
|
import android.util.*;
|
||||||
import android.util.Log;
|
import android.view.*;
|
||||||
import android.view.View;
|
|
||||||
|
import com.android.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a simple white circle on which the numbers will be drawn.
|
* Draws a simple white circle on which the numbers will be drawn.
|
||||||
@@ -16,30 +16,20 @@
|
|||||||
|
|
||||||
package com.android.datetimepicker.time;
|
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 com.android.*;
|
||||||
import android.animation.ObjectAnimator;
|
import com.android.datetimepicker.*;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The primary layout to hold the circular picker, and the am/pm buttons. This view well measure
|
* 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;
|
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 com.android.*;
|
||||||
import android.animation.ObjectAnimator;
|
import com.android.datetimepicker.*;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View to show what number is selected. This will draw a blue circle over the number, with a blue
|
* 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;
|
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 com.android.*;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A view to show a series of numbers in a circular pattern.
|
* A view to show a series of numbers in a circular pattern.
|
||||||
@@ -16,37 +16,25 @@
|
|||||||
|
|
||||||
package com.android.datetimepicker.time;
|
package com.android.datetimepicker.time;
|
||||||
|
|
||||||
import java.text.DateFormatSymbols;
|
import android.animation.*;
|
||||||
import java.util.ArrayList;
|
import android.annotation.*;
|
||||||
import java.util.Locale;
|
import android.app.ActionBar.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
|
|
||||||
import android.animation.ObjectAnimator;
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.*;
|
import android.app.*;
|
||||||
import android.app.ActionBar;
|
import android.content.*;
|
||||||
import android.app.ActionBar.LayoutParams;
|
import android.content.res.*;
|
||||||
import android.content.Context;
|
import android.os.*;
|
||||||
import android.content.res.ColorStateList;
|
import androidx.appcompat.app.*;
|
||||||
import android.content.res.Resources;
|
import android.util.*;
|
||||||
import android.os.Bundle;
|
import android.view.*;
|
||||||
import android.support.v7.app.*;
|
import android.view.View.*;
|
||||||
import android.util.Log;
|
import android.widget.*;
|
||||||
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.*;
|
||||||
import com.android.datetimepicker.Utils;
|
import com.android.datetimepicker.*;
|
||||||
import com.android.datetimepicker.time.RadialPickerLayout.OnValueSelectedListener;
|
import com.android.datetimepicker.time.RadialPickerLayout.*;
|
||||||
|
|
||||||
|
import java.text.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog to set a time.
|
* Dialog to set a time.
|
||||||
@@ -75,6 +63,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
|
|||||||
private static final int PULSE_ANIMATOR_DELAY = 300;
|
private static final int PULSE_ANIMATOR_DELAY = 300;
|
||||||
|
|
||||||
private OnTimeSetListener mCallback;
|
private OnTimeSetListener mCallback;
|
||||||
|
private DialogInterface.OnDismissListener dismissListener;
|
||||||
|
|
||||||
private HapticFeedbackController mHapticFeedbackController;
|
private HapticFeedbackController mHapticFeedbackController;
|
||||||
|
|
||||||
@@ -128,7 +117,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
|
|||||||
*/
|
*/
|
||||||
void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute);
|
void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute);
|
||||||
|
|
||||||
void onTimeCleared(RadialPickerLayout view);
|
default void onTimeCleared(RadialPickerLayout view) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimePickerDialog() {
|
public TimePickerDialog() {
|
||||||
@@ -1010,4 +999,15 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
|
|||||||
return false;
|
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 |
@@ -24,16 +24,36 @@
|
|||||||
<dimen name="color_swatch_margins_large">8dip</dimen>
|
<dimen name="color_swatch_margins_large">8dip</dimen>
|
||||||
<dimen name="color_swatch_margins_small">4dip</dimen>
|
<dimen name="color_swatch_margins_small">4dip</dimen>
|
||||||
|
|
||||||
<item name="circle_radius_multiplier" format="float" type="string" translatable="false">0.82</item>
|
<item name="circle_radius_multiplier" format="float" translatable="false" type="string">
|
||||||
<item name="circle_radius_multiplier_24HourMode" format="float" type="string" translatable="false">0.85</item>
|
0.82
|
||||||
<item name="selection_radius_multiplier" format="float" type="string" translatable="false">0.16</item>
|
</item>
|
||||||
<item name="ampm_circle_radius_multiplier" format="float" type="string" translatable="false">0.19</item>
|
<item name="circle_radius_multiplier_24HourMode" format="float" translatable="false" type="string">
|
||||||
<item name="numbers_radius_multiplier_normal" format="float" type="string" translatable="false">0.81</item>
|
0.85
|
||||||
<item name="numbers_radius_multiplier_inner" format="float" type="string" translatable="false">0.60</item>
|
</item>
|
||||||
<item name="numbers_radius_multiplier_outer" format="float" type="string" translatable="false">0.83</item>
|
<item name="selection_radius_multiplier" format="float" translatable="false" type="string">
|
||||||
<item name="text_size_multiplier_normal" format="float" type="string" translatable="false">0.17</item>
|
0.16
|
||||||
<item name="text_size_multiplier_inner" format="float" type="string" translatable="false">0.14</item>
|
</item>
|
||||||
<item name="text_size_multiplier_outer" format="float" type="string" translatable="false">0.11</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="time_label_size">60sp</dimen>
|
||||||
<dimen name="extra_time_label_margin">-30dp</dimen>
|
<dimen name="extra_time_label_margin">-30dp</dimen>
|
||||||
@@ -41,12 +61,12 @@
|
|||||||
<dimen name="done_label_size">14sp</dimen>
|
<dimen name="done_label_size">14sp</dimen>
|
||||||
<dimen name="ampm_left_padding">6dip</dimen>
|
<dimen name="ampm_left_padding">6dip</dimen>
|
||||||
<dimen name="separator_padding">4dip</dimen>
|
<dimen name="separator_padding">4dip</dimen>
|
||||||
<dimen name="header_height">96dip</dimen>
|
<dimen name="header_height">80dip</dimen>
|
||||||
<dimen name="footer_height">48dip</dimen>
|
<dimen name="footer_height">48dip</dimen>
|
||||||
<dimen name="minimum_margin_sides">48dip</dimen>
|
<dimen name="minimum_margin_sides">48dip</dimen>
|
||||||
<dimen name="minimum_margin_top_bottom">24dip</dimen>
|
<dimen name="minimum_margin_top_bottom">24dip</dimen>
|
||||||
<dimen name="picker_dimen">270dip</dimen>
|
<dimen name="picker_dimen">250dip</dimen>
|
||||||
<dimen name="date_picker_component_width">270dp</dimen>
|
<dimen name="date_picker_component_width">250dp</dimen>
|
||||||
<dimen name="date_picker_header_height">30dp</dimen>
|
<dimen name="date_picker_header_height">30dp</dimen>
|
||||||
<dimen name="selected_calendar_layout_height">155dp</dimen>
|
<dimen name="selected_calendar_layout_height">155dp</dimen>
|
||||||
<dimen name="date_picker_view_animator_height">270dp</dimen>
|
<dimen name="date_picker_view_animator_height">270dp</dimen>
|
||||||
@@ -64,8 +84,8 @@
|
|||||||
<dimen name="year_label_height">64dp</dimen>
|
<dimen name="year_label_height">64dp</dimen>
|
||||||
<dimen name="year_label_text_size">22dp</dimen>
|
<dimen name="year_label_text_size">22dp</dimen>
|
||||||
|
|
||||||
<string name="color_swatch_description" translatable="false">Color <xliff:g id="color_index" example="14">%1$d</xliff:g></string>
|
<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 id="color_index" example="14">%1$d</xliff:g> selected</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 -->
|
<!-- Date and time picker -->
|
||||||
<string name="hour_picker_description" translatable="false">Hours circular slider</string>
|
<string name="hour_picker_description" translatable="false">Hours circular slider</string>
|
||||||
@@ -74,11 +94,56 @@
|
|||||||
<string name="year_picker_description" translatable="false">Year list</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_day" translatable="false">Select month and day</string>
|
||||||
<string name="select_year" translatable="false">Select year</string>
|
<string name="select_year" translatable="false">Select year</string>
|
||||||
<string name="item_is_selected" translatable="false"><xliff:g id="item" example="2013">%1$s</xliff:g> selected</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 id="key" example="4">%1$s</xliff:g> deleted</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_placeholder" translatable="false">--</string>
|
||||||
<string name="time_separator" translatable="false">:</string>
|
<string name="time_separator" translatable="false">:</string>
|
||||||
<string name="radial_numbers_typeface" translatable="false">sans-serif</string>
|
<string name="radial_numbers_typeface" translatable="false">sans-serif</string>
|
||||||
<string name="sans_serif" 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>
|
<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>
|
</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 = 52
|
||||||
|
VERSION_NAME = 1.8.9
|
||||||
|
|
||||||
|
MIN_SDK_VERSION = 21
|
||||||
|
TARGET_SDK_VERSION = 29
|
||||||
|
COMPILE_SDK_VERSION = 29
|
||||||
|
|
||||||
|
DAGGER_VERSION = 2.25.4
|
||||||
|
KOTLIN_VERSION = 1.3.61
|
||||||
|
SUPPORT_LIBRARY_VERSION = 28.0.0
|
||||||
|
AUTO_FACTORY_VERSION = 1.0-beta6
|
||||||
|
BUILD_TOOLS_VERSION = 3.5.3
|
||||||
|
|
||||||
|
org.gradle.parallel=false
|
||||||
|
org.gradle.daemon=true
|
||||||
|
org.gradle.jvmargs=-Xms2048m -Xmx2048m -XX:MaxPermSize=2048m
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
@@ -1,6 +1,5 @@
|
|||||||
#Thu Jun 09 17:53:47 EDT 2016
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-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"
|
||||||
|
}
|
||||||
31
android/uhabits-android/proguard-rules.txt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
-dontobfuscate
|
||||||
|
|
||||||
|
-dontwarn java.**
|
||||||
|
-dontwarn javax.**
|
||||||
|
-dontwarn org.apache.commons.beanutils.*
|
||||||
|
-dontwarn org.codehaus.mojo.**
|
||||||
|
|
||||||
|
-dontnote com.android.**
|
||||||
|
-dontnote com.google.gson.internal.**
|
||||||
|
-dontnote dagger.*
|
||||||
|
-dontnote dalvik.system.**
|
||||||
|
-dontnote javax.inject.**
|
||||||
|
-dontnote org.apache.harmony.xnet.**
|
||||||
|
-dontnote org.isoron.**
|
||||||
|
-dontnote sun.misc.**
|
||||||
|
-dontnote sun.security.**
|
||||||
|
|
||||||
|
-keep class com.getpebble.** { *; }
|
||||||
|
-keep class com.github.paolorotolo.** { *; }
|
||||||
|
-keep class io.socket.** { *; }
|
||||||
|
-keep class okhttp3.** { *; }
|
||||||
|
-keep class okio.** { *; }
|
||||||
|
-keep class org.isoron.** { *; }
|
||||||
|
-keep class sun.misc.Unsafe { *; }
|
||||||
|
-keep class android.support.test.** { *; }
|
||||||
|
-keep class org.mockito.** { *; }
|
||||||
|
-keep class org.junit.** { *; }
|
||||||
|
-keep class kotlin.** { *; }
|
||||||
|
|
||||||
|
|
||||||
|
-dontskipnonpubliclibraryclassmembers
|
||||||