Compare commits
308 Commits
v2.0.2
...
d3c90481be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3c90481be | ||
| f9bb0d7d7b | |||
| f23a1bedee | |||
| dcf31ba115 | |||
|
|
5409a324e8 | ||
|
|
504362e680 | ||
|
|
0ce2f8fae2 | ||
| 2fc6c67432 | |||
| 459cf02642 | |||
| 44cb64601f | |||
| e4b16f6d59 | |||
| 3021e408a7 | |||
| 7649119db7 | |||
| b0a4f26e7a | |||
|
|
dd47d4cf08 | ||
|
|
8912a9d73c | ||
|
|
9bd1c6f685 | ||
|
|
e15e06034c | ||
|
|
a6180a5049 | ||
|
|
7c69b17e77 | ||
|
|
602a40a532 | ||
|
|
e00998f913 | ||
|
|
af5914c2db | ||
|
|
6c5f70638a | ||
|
|
ba88552919 | ||
|
|
8c90c4f68d | ||
|
|
08eb1a600d | ||
|
|
8ea0480d4a | ||
|
|
36ee39589e | ||
|
|
ac7a721940 | ||
|
|
b36ca8673a | ||
|
|
7cfac486f9 | ||
|
|
21dd413ab5 | ||
|
|
39e10638b5 | ||
| d2d45991b0 | |||
| 25aeafb759 | |||
| 9045ae5c24 | |||
| 555873354c | |||
| 2a012619a7 | |||
| d1de3a852b | |||
| f04e37e905 | |||
| 825a5f2cb9 | |||
| 0de6896691 | |||
| 7187214282 | |||
|
|
9d4161a255 | ||
|
|
d82a3c145d | ||
|
|
a6cbd44e42 | ||
|
|
45c62b4ab2 | ||
|
|
d53312d261 | ||
|
|
c453810785 | ||
|
|
999057300b | ||
|
|
2245347e28 | ||
|
|
b79f7850ed | ||
|
|
4469f86e46 | ||
|
|
bdeddb149e | ||
|
|
3b12ec4bfe | ||
| ca4618579e | |||
| 2f13aba22c | |||
| dba5912ecd | |||
| 88b8663484 | |||
| 609886fd09 | |||
| a4db997e06 | |||
|
|
090216ccb7 | ||
|
|
472092d1bf | ||
|
|
6d3791de31 | ||
|
|
24a99d5791 | ||
|
|
6c631b1cf6 | ||
|
|
5d7677b354 | ||
|
|
22820f4f24 | ||
| 1d3bd48535 | |||
|
|
694446b7e3 | ||
|
|
20ae9d247e | ||
|
|
1943fac610 | ||
|
|
a07a50e635 | ||
|
|
8ccb9bbab1 | ||
|
|
743b8d26ad | ||
|
|
c47bd4c328 | ||
|
|
d84abc3a6a | ||
|
|
4ae85f1ec0 | ||
|
|
79d40d1d79 | ||
|
|
1902b8821e | ||
|
|
075542d605 | ||
|
|
0b5894ee6d | ||
|
|
92fb9dbdb6 | ||
|
|
1860abf532 | ||
|
|
3f4780c9fb | ||
|
|
d5d6e4616e | ||
|
|
18e267053d | ||
| 7ba9a7e4e7 | |||
| 5b8a7c39e2 | |||
| d40a5a89cd | |||
| 308d558347 | |||
| 9770ce187a | |||
| 535bc03b70 | |||
| 642e45af89 | |||
| 56d2307b75 | |||
| d875af8a8e | |||
| feeb4f057d | |||
| 1ad5c6b896 | |||
| baee3b9f86 | |||
| e6167baab1 | |||
|
|
1c15e7742e | ||
|
|
072ba63789 | ||
|
|
0fa0daa058 | ||
|
|
5a5ed3d631 | ||
|
|
3dfa376f59 | ||
|
|
7bf74634bb | ||
|
|
fc645a81bc | ||
|
|
35365bbdf3 | ||
|
|
779ef5dbee | ||
|
|
e82994c76b | ||
|
|
4e1d01d8d1 | ||
|
|
5de0fc86e5 | ||
| e26b643423 | |||
| 621534d610 | |||
| a01300e9c6 | |||
| ecb8ce105a | |||
| 32ef3c14f7 | |||
| 4972257635 | |||
| c98cb50baa | |||
| c331f34fa9 | |||
| a1aea532b5 | |||
| 43489aeb4c | |||
| 990c85aedd | |||
|
|
64337b9bee | ||
|
|
8bdfaa2434 | ||
|
|
5f6060858d | ||
| b62e436054 | |||
| bf63b4dbcf | |||
| a82d940bcc | |||
| ba59dc7ca9 | |||
| 181290a0f3 | |||
|
|
d553c2f3f2 | ||
|
|
7776093217 | ||
|
|
b27f3f8540 | ||
| eb041bf6b2 | |||
|
|
e3c53bf07f | ||
|
|
9ddab6ee59 | ||
|
|
2615795402 | ||
|
|
6531445d7f | ||
|
|
4fbf8a8ca2 | ||
|
|
707b2b4380 | ||
|
|
aae85c1170 | ||
|
|
c12a6c6a4d | ||
|
|
b15c02adbf | ||
|
|
9e24128675 | ||
|
|
66c61e2e6c | ||
|
|
7bddfbe5a7 | ||
|
|
8036b10ee6 | ||
|
|
71f400f587 | ||
|
|
79e302f922 | ||
|
|
af7f60fc4d | ||
|
|
7cc4b66dfd | ||
|
|
a9fddf9963 | ||
|
|
36c1504c6a | ||
|
|
d644170141 | ||
|
|
d38f83e961 | ||
|
|
c50c5af497 | ||
|
|
fa3774a32b | ||
|
|
fd124f2a6c | ||
|
|
265b65eb8a | ||
|
|
4c269c55d2 | ||
|
|
c03305120e | ||
|
|
29615b670b | ||
|
|
6ab4a696b6 | ||
|
|
23479c7765 | ||
|
|
6d98f7aafa | ||
| 75078ed52b | |||
| 2a0afedb1d | |||
| 66a2b41250 | |||
|
|
d6a7fa3d7a | ||
|
|
07e55f1c76 | ||
| 4ee5dd910b | |||
| 87f071b5b4 | |||
| bb0b5e8adf | |||
|
|
c79d1e82a5 | ||
|
|
4aebeedec6 | ||
|
|
7de94f2caf | ||
|
|
17ed85fc1b | ||
|
|
4355fb4d68 | ||
|
|
508200abeb | ||
|
|
a29943e783 | ||
|
|
3e6a9181d6 | ||
| 1fe3a3d1ca | |||
|
|
b2951a3475 | ||
|
|
9d3c63cf62 | ||
|
|
65d237254c | ||
|
|
fe1d5c66cb | ||
|
|
113a5028af | ||
|
|
1a56260757 | ||
|
|
697fffbc99 | ||
|
|
804edfa64e | ||
| 2ab6c396d0 | |||
| a55f467339 | |||
|
|
cf682f68c9 | ||
| 0e988e746c | |||
|
|
f119cbf8e7 | ||
|
|
056f5f6fce | ||
|
|
42f6125d5e | ||
|
|
3e20fc4d1d | ||
|
|
1f763feb69 | ||
|
|
6e7ad329fe | ||
|
|
5cb241475d | ||
|
|
27e76c7243 | ||
|
|
576ad04064 | ||
|
|
5f8187ef6d | ||
|
|
f16f919e27 | ||
|
|
736bb8a75e | ||
|
|
de9ad6d4a4 | ||
|
|
1d37ce54ea | ||
|
|
f88f1cfb54 | ||
|
|
fc1478645b | ||
|
|
ffab001b09 | ||
|
|
a58a8005e1 | ||
|
|
c884ada187 | ||
|
|
e3d46ad5a0 | ||
|
|
f4a2c03216 | ||
|
|
f2b8f2f98d | ||
|
|
2c5fd87a2a | ||
|
|
39768f7f04 | ||
|
|
cc3e1ced15 | ||
|
|
2e26cc104e | ||
|
|
42fd0926ef | ||
| ec202aa9a7 | |||
|
|
1fb56c8777 | ||
|
|
a5d4a37da8 | ||
|
|
4804a48549 | ||
|
|
c892a845b4 | ||
|
|
e98064b6a5 | ||
| 57f5f6ed5b | |||
| 79f5b8b7e8 | |||
| f15c660d33 | |||
| 1866743c47 | |||
| 6b9a7917b4 | |||
| 2a5725f382 | |||
| edeabb6ee3 | |||
| 24a9fbe414 | |||
| e5b8c4c3c4 | |||
|
|
a781a1f947 | ||
|
|
13e57b5026 | ||
|
|
f8c7abfff4 | ||
|
|
7fe3ce970c | ||
| 9c395243f4 | |||
|
|
b9eb244b0b | ||
|
|
420a99f1cf | ||
| e756a639ae | |||
|
|
91ff5f7a0c | ||
|
|
67b55a4ecf | ||
|
|
a5ae2eaa63 | ||
|
|
0bc2a8b6d4 | ||
|
|
95a1786c4a | ||
|
|
caa1c9d72e | ||
|
|
a7afe0b309 | ||
|
|
9c03174eef | ||
|
|
da02926fa6 | ||
|
|
250dabfe58 | ||
|
|
0bba3b76bc | ||
|
|
c2479278ba | ||
|
|
499a403a06 | ||
|
|
af5d622339 | ||
|
|
5eeb54bc47 | ||
|
|
e09e899aad | ||
| 69f0fc6c3a | |||
| 705dfb9cbd | |||
| d0ef749f19 | |||
| b54711243f | |||
| 37f03aca37 | |||
|
|
271de59a94 | ||
|
|
0ab55f6f5a | ||
|
|
43921721d4 | ||
|
|
33c88cded3 | ||
|
|
aecce891ea | ||
|
|
2ea98a7756 | ||
|
|
e667872d83 | ||
|
|
3602a614c4 | ||
|
|
d8c5f4d93c | ||
|
|
c9f4df9dae | ||
|
|
feb384bca6 | ||
|
|
71e9160460 | ||
|
|
64966d3c86 | ||
|
|
4787df4074 | ||
| 92291fd919 | |||
| be51538704 | |||
| 89b24911ba | |||
| 1cf71b3973 | |||
| 2fe3b15806 | |||
| 33468bfc1c | |||
|
|
5908692a5c | ||
|
|
693dce8719 | ||
| b232827dfd | |||
|
|
02f9f411ce | ||
| bbf9da44e1 | |||
|
|
ee896fb4f9 | ||
|
|
4d7d8b6206 | ||
|
|
55b841a8b4 | ||
| 7fac86b617 | |||
|
|
c31d42be2d | ||
| 31c09b9c0b | |||
|
|
db91dce57f | ||
| 12c76245e6 | |||
| 59a4d7552c | |||
| 9d7840bdd1 | |||
|
|
32db4e363b | ||
|
|
004bb8d71c | ||
|
|
21a1e88c47 | ||
|
|
86fb718896 | ||
|
|
a4e9b2f874 | ||
|
|
ac924470b8 |
18
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -6,11 +6,16 @@ labels: ''
|
|||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
<!--
|
||||||
|
Please use the template below for your bug report. Issues that do not follow this
|
||||||
|
template, or that do not contain all necessary information (namely, description of
|
||||||
|
the problem, steps to reproduce, phone, phone operating system, and app version)
|
||||||
|
will be closed without further consideration.
|
||||||
|
-->
|
||||||
|
|
||||||
## Pre-submission checklist
|
## Pre-submission checklist
|
||||||
- [ ] I am submitting a bug report, not a feature request.
|
- [ ] I am submitting a bug report, not a feature request.
|
||||||
- [ ] I am running the latest version of Loop Habit Tracker.
|
- [ ] I have searched for similar issues, but did not find any matches.
|
||||||
- [ ] I have have searched for similar issues, but did not find any matches.
|
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
A clear and concise description of what the problem is.
|
A clear and concise description of what the problem is.
|
||||||
@@ -21,10 +26,11 @@ A clear and concise description of what the problem is.
|
|||||||
3. Scroll down to ....
|
3. Scroll down to ....
|
||||||
4. See error
|
4. See error
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
## System information
|
## System information
|
||||||
- Phone: [e.g. Google Pixel 4]
|
- Phone: [e.g. Google Pixel 4]
|
||||||
- Phone Operating System: [e.g. Android 10]
|
- Phone Operating System: [e.g. Android 10]
|
||||||
- App version: [e.g. 1.8.9]
|
- App version: [e.g. 2.0.2]
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
|||||||
2
.github/dependabot.yml
vendored
@@ -3,4 +3,4 @@ updates:
|
|||||||
- package-ecosystem: "gradle"
|
- package-ecosystem: "gradle"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "monthly"
|
||||||
|
|||||||
9
.github/workflows/main.yml
vendored
@@ -13,10 +13,10 @@ jobs:
|
|||||||
- name: Check out source code
|
- name: Check out source code
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
- name: Install Java Development Kit 1.8
|
- name: Install Java Development Kit 11
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 1.8
|
java-version: 11
|
||||||
|
|
||||||
- name: Build Project
|
- name: Build Project
|
||||||
run: ./build.sh build
|
run: ./build.sh build
|
||||||
@@ -55,6 +55,11 @@ jobs:
|
|||||||
name: uhabits-android
|
name: uhabits-android
|
||||||
path: uhabits-android/build/outputs/
|
path: uhabits-android/build/outputs/
|
||||||
|
|
||||||
|
- name: Install flock
|
||||||
|
run: |
|
||||||
|
brew install util-linux
|
||||||
|
echo "/usr/local/opt/util-linux/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
- name: Run Android Tests
|
- name: Run Android Tests
|
||||||
run: ./build.sh android-tests ${{ matrix.api }}
|
run: ./build.sh android-tests ${{ matrix.api }}
|
||||||
|
|
||||||
|
|||||||
10
CHANGELOG.md
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [2.0.3] - 2021-08-21
|
||||||
|
### Fixed
|
||||||
|
- Improve automatic checkmarks for monthly habits (@iSoron, #947)
|
||||||
|
- Fix small theme issues (@iSoron)
|
||||||
|
- Fix ANR on some Samsung phones (@iSoron, #962)
|
||||||
|
- Fix dates before the year 2000 (@iSoron, #967)
|
||||||
|
- Fix notification adding checkmarks to the wrong day (@hiqua, #969)
|
||||||
|
- Fix crashes in widgets (@hiqua, @iSoron, #907, #966, #965)
|
||||||
|
- Fix crash when moving habits (@hiqua, #968)
|
||||||
|
|
||||||
## [2.0.2] - 2021-05-23
|
## [2.0.2] - 2021-05-23
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
10
README.md
@@ -18,7 +18,7 @@ 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="Get it on F-Droid" src="http://i.imgur.com/baSPE7X.png" height="75px"/></a>
|
<a href="https://f-droid.org/app/org.isoron.uhabits"><img alt="Get it on F-Droid" src="https://i.imgur.com/baSPE7X.png" height="75px"/></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
@@ -90,7 +90,7 @@ contribute, even if you are not a software developer.
|
|||||||
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
|
||||||
contribute with code. Please see `docs/GUIDELINES.md`.
|
contribute with code. Please see the [guidelines](https://github.com/iSoron/uhabits/blob/dev/docs/GUIDELINES.md).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ contribute, even if you are not a software developer.
|
|||||||
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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
[screen1]: screenshots/1.png
|
[screen1]: screenshots/1.png
|
||||||
[screen2]: screenshots/2.png
|
[screen2]: screenshots/2.png
|
||||||
@@ -123,9 +123,9 @@ contribute, even if you are not a software developer.
|
|||||||
[screen4th]: screenshots/4.thumb.png
|
[screen4th]: screenshots/4.thumb.png
|
||||||
[screen5th]: screenshots/5.thumb.png
|
[screen5th]: screenshots/5.thumb.png
|
||||||
[screen6th]: screenshots/6.thumb.png
|
[screen6th]: screenshots/6.thumb.png
|
||||||
[poedit]: http://translate.loophabits.org
|
[poedit]: https://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]: https://f-droid.org/app/org.isoron.uhabits
|
||||||
[build]: https://github.com/iSoron/uhabits/blob/dev/docs/BUILD.md
|
[build]: https://github.com/iSoron/uhabits/blob/dev/docs/BUILD.md
|
||||||
[beta]: https://play.google.com/apps/testing/org.isoron.uhabits
|
[beta]: https://play.google.com/apps/testing/org.isoron.uhabits
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
plugins {
|
plugins {
|
||||||
val kotlinVersion = "1.4.21"
|
val kotlinVersion = "1.6.10"
|
||||||
id("com.android.application") version ("4.1.0") apply (false)
|
id("com.android.application") version ("7.0.3") apply (false)
|
||||||
id("org.jetbrains.kotlin.android") version kotlinVersion apply (false)
|
id("org.jetbrains.kotlin.android") version kotlinVersion apply (false)
|
||||||
id("org.jetbrains.kotlin.kapt") version kotlinVersion apply (false)
|
id("org.jetbrains.kotlin.kapt") version kotlinVersion apply (false)
|
||||||
id("org.jetbrains.kotlin.android.extensions") version kotlinVersion apply (false)
|
id("org.jetbrains.kotlin.android.extensions") version kotlinVersion apply (false)
|
||||||
id("org.jetbrains.kotlin.multiplatform") version kotlinVersion apply (false)
|
id("org.jetbrains.kotlin.multiplatform") version kotlinVersion apply (false)
|
||||||
id("org.jlleitschuh.gradle.ktlint") version "10.0.0"
|
id("org.jlleitschuh.gradle.ktlint") version "10.3.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply {
|
apply {
|
||||||
@@ -15,11 +15,9 @@ apply {
|
|||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven(url = "https://plugins.gradle.org/m2/")
|
maven(url = "https://plugins.gradle.org/m2/")
|
||||||
maven(url = "https://oss.sonatype.org/content/repositories/snapshots/")
|
maven(url = "https://oss.sonatype.org/content/repositories/snapshots/")
|
||||||
maven(url = "https://kotlin.bintray.com/ktor")
|
maven(url = "https://jitpack.io")
|
||||||
maven(url = "https://kotlin.bintray.com/kotlin-js-wrappers")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ The repository will be downloaded to the directory `uhabits`.
|
|||||||
2. When the IDE asks you for the project location, select `uhabits` and click "Ok".
|
2. When the IDE asks you for the project location, select `uhabits` and click "Ok".
|
||||||
3. Android Studio will spend some time indexing the project. When this is complete, click the toolbar icon "Sync Project with Gradle File", located near the right corner of the top toolbar.
|
3. Android Studio will spend some time indexing the project. When this is complete, click the toolbar icon "Sync Project with Gradle File", located near the right corner of the top toolbar.
|
||||||
4. The operation will likely fail several times due to missing Android SDK components. Each time it fails, click the link "Install missing platforms", "Install build tools", etc, and try again.
|
4. The operation will likely fail several times due to missing Android SDK components. Each time it fails, click the link "Install missing platforms", "Install build tools", etc, and try again.
|
||||||
5. To test the application, create a virtual Android device using the menu "Tools" and "AVD Manager". The default options should work fine, but free to customize the device.
|
5. To test the application, create a virtual Android device using the menu "Tools" and "AVD Manager". The default options should work fine, but feel free to customize the device.
|
||||||
6. Click the menu "Run" and "uhabits-android". The application should launch.
|
6. Click the menu "Run" and "uhabits-android". The application should launch.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
org.gradle.parallel=false
|
org.gradle.parallel=false
|
||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
org.gradle.jvmargs=-Xms2048m -Xmx2048m -XX:MaxPermSize=2048m
|
org.gradle.jvmargs=-Xms2048m -Xmx2048m
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,5 @@
|
|||||||
#Sat Nov 28 09:55:24 CST 2020
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip
|
|
||||||
|
|||||||
2
gradlew
vendored
@@ -82,6 +82,7 @@ esac
|
|||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
@@ -129,6 +130,7 @@ fi
|
|||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
|||||||
25
gradlew.bat
vendored
@@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
|
|||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@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"
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto init
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
@@ -51,7 +54,7 @@ goto fail
|
|||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto init
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
@@ -61,28 +64,14 @@ echo location of your Java installation.
|
|||||||
|
|
||||||
goto fail
|
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
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@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%
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
pluginManagement {
|
pluginManagement {
|
||||||
repositories {
|
repositories {
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
jcenter()
|
|
||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
resolutionStrategy.eachPlugin {
|
resolutionStrategy.eachPlugin {
|
||||||
|
|||||||
@@ -1,350 +1,377 @@
|
|||||||
Name,Languages,"Translated (Words)","Target Words","Approved (Words)",Voted,"""+"" votes received","""-"" votes received","Winning (Words)"
|
Name,Languages,"Translated (Words)","Target Words","Approved (Words)",Voted,"""+"" votes received","""-"" votes received","Winning (Words)",Joined
|
||||||
"Alinson Xavier (iSoron)","Portuguese, Brazilian; Japanese; Spanish; Portuguese; Italian; Chinese Simplified; French; Hungarian; German; Arabic; Hindi; Slovenian; Catalan; Greek; Korean; Bulgarian; Chinese Traditional; Polish; Russian; Serbian (Cyrillic); Turkish; Ukrainian; Czech; Indonesian; Croatian; Danish; Dutch; Romanian; Swedish; Basque; Persian; Vietnamese; Telugu; Finnish; Tamil; Afrikaans; Esperanto; Hebrew",14790,17211,1272,0,1707,70,4259
|
"Alinson Xavier (iSoron)","Portuguese, Brazilian; Japanese; Spanish; Portuguese; Italian; Chinese Simplified; French; Hungarian; German; Arabic; Hindi; Slovenian; Catalan; Greek; Korean; Bulgarian; Chinese Traditional; Polish; Russian; Serbian (Cyrillic); Turkish; Ukrainian; Czech; Indonesian; Croatian; Danish; Dutch; Romanian; Swedish; Basque; Persian; Finnish; Vietnamese; Telugu; Tamil; Afrikaans; Esperanto; Hebrew",14808,17227,1282,0,1779,80,4274,"2016-03-05 18:35:27"
|
||||||
"Slobodan Simić (Слободан Симић) (slsimic)","Serbian (Latin); Serbian (Cyrillic)",2053,1830,2105,12,33,0,1982
|
"Slobodan Simić (Слободан Симић) (slsimic)","Serbian (Latin); Serbian (Cyrillic)",2054,1831,2114,12,33,0,1991,"2021-02-03 14:26:07"
|
||||||
"Oglaigh Rystard (oglaignaheireann)","Ukrainian; Portuguese; Catalan; Greek; Basque; Romanian; Italian",1103,1037,1327,1,13,6,954
|
"Oglaigh Rystard (oglaignaheireann)","Ukrainian; Portuguese; Catalan; Greek; Basque; Romanian; Italian",1103,1037,1327,1,13,6,954,"2017-03-31 09:13:19"
|
||||||
"David (Cliff122)",Swedish,1040,1019,725,6,0,0,700
|
"David (Cliff122)",Swedish,1040,1019,725,6,0,0,700,"2020-01-21 13:56:55"
|
||||||
dukelc,Slovak,919,880,0,0,0,0,0
|
"Omer I.S. (omeritzics)",Hebrew,1000,900,1097,14,1,0,946,"2020-10-11 20:10:51"
|
||||||
KMakoto,"Chinese Traditional",745,1146,949,0,0,0,745
|
dukelc,Slovak,919,880,0,0,0,0,0,"2020-08-27 14:02:41"
|
||||||
"Evren (evrenkiymaz)",Turkish,688,604,0,71,4,0,0
|
"Intan Ayunda (Intan_Ayunda)",Indonesian,800,793,962,0,0,0,711,"2020-10-14 07:51:58"
|
||||||
"David Nos (david.nos)","Catalan; Spanish",667,731,0,0,0,0,0
|
KMakoto,"Chinese Traditional",745,1146,949,0,0,0,745,"2019-10-22 04:19:52"
|
||||||
androide74,Italian,644,659,0,2,0,0,0
|
"Evren (evrenkiymaz)",Turkish,688,604,0,71,5,1,0,"2020-10-04 03:39:16"
|
||||||
Tomairuka,Japanese,633,1636,909,43,0,0,564
|
"David Nos (david.nos)","Catalan; Spanish",667,731,0,0,1,0,0,"2020-01-04 10:15:36"
|
||||||
"Dmitriy Bogdanov (di72nn)",Russian,625,572,1175,0,34,0,519
|
"Antti Kallio (antti.kallio)",Finnish,650,525,0,0,0,0,0,"2021-07-03 05:54:44"
|
||||||
reyhoon,Persian,624,759,0,1,1,0,0
|
androide74,Italian,644,659,0,2,0,0,0,"2020-02-06 15:46:28"
|
||||||
Osoitz,Basque,610,545,0,9,0,0,3
|
Tomairuka,Japanese,633,1636,909,43,0,0,564,"2020-12-12 12:14:22"
|
||||||
fabian.bouchal,German,548,527,0,6,0,3,72
|
"Dmitriy Bogdanov (di72nn)",Russian,625,572,1197,0,36,0,515,"2017-03-31 10:00:48"
|
||||||
boban77,Czech,509,461,0,2,0,0,0
|
reyhoon,Persian,624,759,0,1,3,1,0,"2020-10-01 18:17:23"
|
||||||
"Yoav Argov (YoavArgov)",Hebrew,501,461,0,0,1,8,3
|
Osoitz,Basque,610,545,0,9,0,0,3,"2018-01-23 14:07:47"
|
||||||
REMOVED_USER,Norwegian,501,498,501,0,148,0,501
|
"Saeed Esmaili (saaeed.es20)",Persian,568,774,0,5,4,0,0,"2020-11-26 15:41:15"
|
||||||
"Martim Parente (Sharlimar)",Portuguese,497,534,0,38,0,0,0
|
fabian.bouchal,German,548,527,0,6,0,3,72,"2020-01-07 06:43:37"
|
||||||
"chrrris1987 (Chrrris1987)",Dutch,467,478,0,23,0,0,0
|
boban77,Czech,509,461,0,2,0,0,0,"2020-04-30 13:18:24"
|
||||||
"Huy Ngo (huyngo)",Vietnamese,461,695,0,1,0,0,0
|
"Yoav Argov (YoavArgov)",Hebrew,501,461,0,0,1,8,103,"2017-04-28 07:23:01"
|
||||||
"黄克 (hk13127)","Chinese Simplified",461,765,0,1,0,0,0
|
REMOVED_USER,Norwegian,501,498,501,0,148,0,501,"2017-07-05 19:02:25"
|
||||||
"Arkadiusz Bubak (epitek)",Polish,458,416,29,24,0,3,0
|
"Martim Parente (Sharlimar)",Portuguese,497,534,0,38,0,0,0,"2020-08-26 10:22:11"
|
||||||
marco.baturan,Esperanto,452,452,0,0,0,0,0
|
"chrrris1987 (Chrrris1987)",Dutch,467,478,0,23,0,0,0,"2020-02-03 05:26:04"
|
||||||
"Sief Tarek (sieftarek135)",Arabic,447,455,0,0,0,0,0
|
"Huy Ngo (huyngo)",Vietnamese,461,695,0,1,0,0,0,"2020-01-26 11:58:36"
|
||||||
"Omer I.S. (omeritzics)",Hebrew,428,366,0,14,1,0,0
|
"黄克 (hk13127)","Chinese Simplified",461,765,0,1,0,0,24,"2020-01-17 23:16:03"
|
||||||
"Samuel Guay (SamGuay)",French,426,486,0,6,0,0,0
|
"Arkadiusz Bubak (epitek)",Polish,458,416,29,24,0,3,0,"2020-11-05 05:11:58"
|
||||||
"Diana Karaseva (Sun_Dianka)",Russian,399,373,0,10,1,0,221
|
marco.baturan,Esperanto,452,452,0,0,0,0,0,"2020-06-23 02:49:46"
|
||||||
"Alexander Jansson (dalecarlian)",Swedish,396,406,507,0,0,3,399
|
"Sief Tarek (sieftarek135)",Arabic,447,455,0,0,0,0,0,"2021-02-07 14:35:21"
|
||||||
luiandresgonzalez,Spanish,383,403,0,1,0,0,0
|
"Samuel Guay (SamGuay)",French,426,486,0,6,0,0,0,"2020-06-25 07:14:38"
|
||||||
"Thamara Andrade (tkcandrade)","Portuguese, Brazilian",380,387,0,0,1,0,252
|
"Diana Karaseva (Sun_Dianka)",Russian,399,373,0,10,1,0,209,"2020-01-30 06:40:02"
|
||||||
"Sølv Ræven (soelvraeven)",Danish,370,370,0,0,0,0,0
|
"Alexander Jansson (dalecarlian)",Swedish,396,406,507,0,0,3,399,"2017-06-21 01:37:32"
|
||||||
"Isti (eisti)",Hungarian,367,329,0,0,0,0,0
|
luiandresgonzalez,Spanish,383,403,0,1,28,0,0,"2020-07-11 14:20:44"
|
||||||
"Anh Quân (dangquanuet)",Vietnamese,362,530,0,42,1,0,0
|
"Thamara Andrade (tkcandrade)","Portuguese, Brazilian",380,387,0,0,1,0,252,"2020-01-09 19:35:48"
|
||||||
gapszi,Hungarian,348,301,0,86,0,0,0
|
"Sølv Ræven (soelvraeven)",Danish,370,370,0,0,0,0,0,"2020-11-28 16:46:18"
|
||||||
"Mahdi Nasiri (mahdi.nasiri)",Persian,343,465,0,39,3,0,0
|
"Isti (eisti)",Hungarian,367,329,0,0,0,0,0,"2020-12-03 12:02:51"
|
||||||
Seoyul,Korean,339,825,0,0,27,0,0
|
"Anh Quân (dangquanuet)",Vietnamese,362,530,0,42,2,0,0,"2017-10-29 12:27:44"
|
||||||
"Magimai Prakasam (magimai)",Tamil,336,831,0,12,0,0,0
|
gapszi,Hungarian,348,301,0,86,0,0,0,"2019-04-08 01:35:54"
|
||||||
"Michael Malak (MichaelKMalak)",Arabic,304,271,0,0,0,0,0
|
JY3,"Chinese Simplified",345,585,278,0,1,0,207,"2021-03-08 08:53:35"
|
||||||
"Elina Salminen (salminen.elina.m)",Finnish,297,227,0,0,0,0,0
|
"Mahdi Nasiri (mahdi.nasiri)",Persian,343,465,0,39,3,1,0,"2017-07-14 09:17:25"
|
||||||
ayane.m,Japanese,292,863,0,1,2,0,22
|
Seoyul,Korean,339,825,0,0,27,0,0,"2017-06-21 08:11:39"
|
||||||
"Marius Teufelweich (teufelweich)",German,249,256,606,4,0,0,146
|
"Magimai Prakasam (magimai)",Tamil,336,831,0,12,0,0,0,"2018-04-15 21:16:08"
|
||||||
cobalt59,German,237,234,0,1,23,1,132
|
"Michael Malak (MichaelKMalak)",Arabic,304,271,0,0,0,0,0,"2020-05-26 19:47:58"
|
||||||
"QWERT (lurenjia01)","Chinese Simplified",236,407,0,0,0,0,0
|
"Elina Salminen (salminen.elina.m)",Finnish,297,227,0,0,0,0,0,"2021-01-06 01:28:57"
|
||||||
beriain,Basque,234,235,0,0,2,0,0
|
ayane.m,Japanese,292,863,0,1,3,0,22,"2019-11-20 03:28:26"
|
||||||
"Intan Ayunda (Intan_Ayunda)",Indonesian,228,239,336,0,0,0,135
|
Blinkin,Dutch,284,318,0,1,0,0,0,"2021-06-14 10:30:05"
|
||||||
pnhpnh,Vietnamese,225,343,0,1,3,0,0
|
"Marius Teufelweich (teufelweich)",German,249,256,606,4,2,0,146,"2021-03-12 04:11:38"
|
||||||
"Dika Fitrian Dwi Putra (OsamuDazai)",Indonesian,221,215,0,0,0,0,33
|
cobalt59,German,237,234,0,1,24,1,132,"2017-06-05 05:18:33"
|
||||||
easyrepro,Telugu,214,297,0,0,0,0,0
|
"QWERT (lurenjia01)","Chinese Simplified",236,407,0,0,8,0,19,"2020-03-08 01:46:25"
|
||||||
taras-ko,Ukrainian,211,183,0,1,4,0,19
|
beriain,Basque,234,235,0,0,2,0,0,"2017-03-31 15:42:28"
|
||||||
sojusnik,German,207,200,1,0,30,0,66
|
pnhpnh,Vietnamese,225,343,0,1,3,0,0,"2017-11-27 12:06:07"
|
||||||
axmed99,Ukrainian,203,177,0,40,0,0,53
|
"Dika Fitrian Dwi Putra (OsamuDazai)",Indonesian,221,215,0,0,0,0,48,"2020-07-13 04:40:27"
|
||||||
"Heru Yen (heruyen)",Indonesian,201,201,0,0,0,0,2
|
easyrepro,Telugu,214,297,0,0,0,0,0,"2020-06-12 12:52:10"
|
||||||
Ishmaeel,Turkish,193,174,0,129,6,0,0
|
taras-ko,Ukrainian,211,183,0,1,4,0,19,"2017-10-26 16:52:22"
|
||||||
fbruna17,Danish,181,179,0,1,0,0,0
|
sojusnik,German,207,200,1,0,30,0,66,"2017-04-03 17:11:56"
|
||||||
"Omry Cohen (omrycohen)",Hebrew,175,156,0,1,0,0,0
|
axmed99,Ukrainian,203,177,0,40,0,0,53,"2021-04-01 03:56:20"
|
||||||
Bryanx,Dutch,174,165,0,5,0,0,0
|
"Heru Yen (heruyen)",Indonesian,201,201,0,0,0,0,25,"2020-06-29 18:39:15"
|
||||||
"Pierre GALIEGUE (pierre.galiegue)",French,171,194,0,24,2,0,0
|
"Vijaykumar Borkar (vjkumar)",Hindi,200,364,0,11,0,0,0,"2021-08-06 16:12:15"
|
||||||
DionysosDV,Greek,165,153,0,0,0,0,0
|
Ishmaeel,Turkish,193,174,0,129,6,0,0,"2017-10-04 03:54:00"
|
||||||
"Gustavo Lima (GustavoLima)",Portuguese,158,177,0,1,4,10,0
|
"Aputsiak Niels Janussen (aputtu)",Danish,187,200,0,0,0,0,0,"2019-08-28 05:47:42"
|
||||||
"Aputsiak Niels Janussen (aputtu)",Danish,153,171,0,0,0,0,0
|
_translator,French,181,206,0,11,0,0,0,"2021-07-06 07:54:12"
|
||||||
plitwin,Polish,145,128,0,1,16,0,26
|
fbruna17,Danish,181,179,0,1,0,0,0,"2021-01-28 15:48:47"
|
||||||
"Lương Vĩnh Khang (LuongVinhKhang)",Vietnamese,144,256,0,0,46,1,0
|
"Omry Cohen (omrycohen)",Hebrew,175,156,0,1,0,0,33,"2021-01-18 07:33:23"
|
||||||
azzamsa,Indonesian,142,136,0,48,0,1,50
|
Bryanx,Dutch,174,165,0,5,0,0,0,"2019-11-21 17:08:12"
|
||||||
"Neysa Nasywa (neysanasywa)",Indonesian,140,141,0,0,0,0,35
|
"Pierre GALIEGUE (pierre.galiegue)",French,171,194,0,24,4,0,0,"2020-08-16 11:41:35"
|
||||||
JY3,"Chinese Simplified",136,217,80,0,0,0,80
|
bruhwut,Vietnamese,171,268,0,1,0,0,0,"2021-05-21 07:16:30"
|
||||||
"Mohammed Imthath (mimthath4)",Tamil,136,274,0,0,11,0,0
|
DionysosDV,Greek,165,153,0,0,0,0,0,"2021-02-27 19:05:25"
|
||||||
roptat,French,132,154,0,112,88,5,0
|
"Gustavo Lima (GustavoLima)",Portuguese,158,177,0,1,4,10,0,"2020-08-26 10:35:05"
|
||||||
"Trần Thái (tranhoangthai2001)",Vietnamese,127,186,0,8,1,0,0
|
oscfd,Spanish,155,166,0,1,4,0,0,"2021-05-21 17:58:22"
|
||||||
"OP Smosher (teenwolffan44)","Serbian (Cyrillic)",124,122,0,0,0,0,18
|
plitwin,Polish,145,128,0,1,16,0,26,"2021-01-20 06:18:37"
|
||||||
4001982248998,Esperanto,122,119,0,0,0,0,0
|
"Lương Vĩnh Khang (LuongVinhKhang)",Vietnamese,144,256,0,0,46,1,0,"2017-08-10 10:05:58"
|
||||||
"StoP4Me (Lcqp)",Romanian,121,119,0,0,3,0,0
|
azzamsa,Indonesian,142,136,0,48,0,1,26,"2017-06-16 18:29:45"
|
||||||
Sebastian05067,Spanish,114,133,0,55,18,0,0
|
"yoding (yodingc)","Chinese Traditional; Chinese Simplified",141,271,0,10,0,0,0,"2021-07-07 01:45:45"
|
||||||
"Tanya (MagicUnderHood)",Russian,114,98,0,19,0,0,32
|
"Neysa Nasywa (neysanasywa)",Indonesian,140,141,0,0,0,0,60,"2020-11-18 10:32:10"
|
||||||
REMOVED_USER,Arabic,111,106,0,22,21,2,0
|
"Mohammed Imthath (mimthath4)",Tamil,136,274,0,0,11,0,0,"2018-02-15 22:41:15"
|
||||||
mohmans,Arabic,109,103,0,2,0,0,0
|
roptat,French,132,154,0,112,89,5,0,"2017-04-19 16:54:47"
|
||||||
"Ivan Krušlin (krux3r)",Croatian,108,122,503,0,0,0,108
|
"Trần Thái (tranhoangthai2001)",Vietnamese,127,186,0,8,1,0,0,"2018-03-01 10:51:39"
|
||||||
"Iabin Arteaga (iabin)",Spanish,108,111,0,4,20,0,0
|
"OP Smosher (teenwolffan44)","Serbian (Cyrillic)",124,122,0,0,0,0,18,"2020-11-05 09:41:35"
|
||||||
2kaafone,Finnish,105,90,0,0,0,0,0
|
4001982248998,Esperanto,122,119,0,0,0,0,0,"2017-10-08 04:13:02"
|
||||||
"Adam Jurkiewicz (hasztagg)",Polish,104,105,529,0,0,0,104
|
"StoP4Me (Lcqp)",Romanian,121,119,0,0,3,0,0,"2018-05-06 18:51:59"
|
||||||
"Saeed Esmaili (saaeed.es20)",Persian,104,141,0,0,0,0,0
|
alalloush,Arabic,118,129,0,2,2,0,0,"2017-03-31 12:37:17"
|
||||||
"just a name bro (justanamebr0)",Danish,98,109,0,0,1,0,0
|
"Eilif Adelvice (adelvice)",Spanish,116,126,0,96,0,0,0,"2021-08-05 07:20:21"
|
||||||
"Nam Nguyen (namnl2706)",Vietnamese,95,137,0,0,0,0,0
|
Sebastian05067,Spanish,114,133,0,55,28,0,0,"2017-05-14 00:48:16"
|
||||||
"손유정 (yuwon1213)",Korean,95,57,0,0,0,0,0
|
"Tanya (MagicUnderHood)",Russian,114,98,0,19,0,0,54,"2019-04-21 10:44:03"
|
||||||
ranmagen,Hebrew,91,78,0,0,0,0,0
|
REMOVED_USER,Arabic,111,106,0,22,21,2,0,"2018-01-05 07:01:45"
|
||||||
LoneWanderer,"Chinese Traditional",90,137,0,4,0,0,0
|
mohmans,Arabic,109,103,0,2,0,0,0,"2020-11-23 02:48:00"
|
||||||
"Vo - (voyl)","Chinese Traditional",89,126,0,0,5,0,0
|
"Iabin Arteaga (iabin)",Spanish,108,111,0,4,21,0,0,"2017-08-26 21:08:54"
|
||||||
ikkaz,Indonesian,89,84,0,5,0,0,4
|
"Ivan Krušlin (krux3r)",Croatian,108,122,503,0,0,0,108,"2017-03-31 09:15:24"
|
||||||
"Irene K (Heaun)",Korean,88,75,0,25,0,0,0
|
2kaafone,Finnish,105,90,0,0,0,0,0,"2019-08-12 06:58:48"
|
||||||
"Kumar Anand (kumar0500)",Hindi,87,125,0,0,0,0,0
|
"Adam Jurkiewicz (hasztagg)",Polish,104,105,529,0,0,0,104,"2017-03-31 09:50:51"
|
||||||
Prosta4ok_ua,Ukrainian,87,84,0,1,0,0,17
|
"just a name bro (justanamebr0)",Danish,98,109,0,0,1,0,0,"2019-06-19 11:57:55"
|
||||||
"Ohad Edri (ohadalte)",Hebrew,85,79,0,0,1,3,0
|
"Nam Nguyen (namnl2706)",Vietnamese,95,137,0,0,0,0,0,"2020-08-18 23:02:33"
|
||||||
helectron,Persian,84,102,0,1,0,0,0
|
"손유정 (yuwon1213)",Korean,95,57,0,0,0,0,0,"2021-03-30 05:25:33"
|
||||||
"Radu Cebotari (wildProgrammer)",Romanian,84,92,0,1,0,0,0
|
ranmagen,Hebrew,91,78,0,0,0,0,0,"2021-02-16 05:44:31"
|
||||||
"Israa Z (sosozozo)",Arabic,79,87,0,43,11,0,3
|
LoneWanderer,"Chinese Traditional",90,137,0,4,0,0,0,"2020-09-29 05:24:48"
|
||||||
"Sofia Neves (sofiasonev)","Portuguese, Brazilian",79,84,0,1,0,0,46
|
"Vo - (voyl)","Chinese Traditional",89,126,0,0,5,0,0,"2020-09-02 23:34:42"
|
||||||
Tiralka,French,79,91,0,92,1,0,0
|
ikkaz,Indonesian,89,84,0,5,0,0,4,"2019-09-02 19:58:54"
|
||||||
"Jacob Roller (jdr28070)",Korean,79,61,0,0,0,0,0
|
"Irene K (Heaun)",Korean,88,75,0,25,0,0,0,"2020-03-16 11:31:12"
|
||||||
"Toni Mustonen (toni.mustonen)",Finnish,78,72,0,0,0,0,0
|
Prosta4ok_ua,Ukrainian,87,84,0,1,0,0,17,"2020-01-23 19:43:41"
|
||||||
"Michael (quelbs)",German,76,75,0,1,0,0,39
|
"Kumar Anand (kumar0500)",Hindi,87,125,0,0,0,0,0,"2020-11-07 02:46:09"
|
||||||
"Fauz Aladeem (topfauz)",Arabic,76,77,0,0,0,1,0
|
"Ohad Edri (ohadalte)",Hebrew,85,79,0,0,1,3,18,"2020-07-04 03:42:09"
|
||||||
RealDonald,Dutch,67,69,0,121,10,0,0
|
helectron,Persian,84,102,0,1,0,0,0,"2021-03-02 04:10:51"
|
||||||
sirekanyan,"Armenian; Russian",66,65,0,0,0,0,0
|
"Radu Cebotari (wildProgrammer)",Romanian,84,92,0,1,0,0,0,"2020-02-05 01:20:00"
|
||||||
"Константин К. (kocyak1991)",Russian,64,60,0,0,1,2,0
|
"Sofia Neves (sofiasonev)","Portuguese, Brazilian",79,84,0,1,0,0,46,"2020-03-12 18:19:46"
|
||||||
"Laura Sophie (laurasophie20)",German,62,67,0,4,0,0,0
|
"Jacob Roller (jdr28070)",Korean,79,61,0,0,0,0,0,"2020-01-03 11:36:40"
|
||||||
raden20,Indonesian,61,62,177,0,1,0,64
|
Tiralka,French,79,91,0,92,1,0,0,"2018-02-09 18:39:01"
|
||||||
"Jan Wojtecki (j4nw)",Polish,58,46,0,0,0,0,26
|
"Israa Z (sosozozo)",Arabic,79,87,0,43,12,0,3,"2017-11-27 14:10:50"
|
||||||
"Deepak Bharathi (deepakbharathi1994)",Tamil,56,107,0,0,11,4,0
|
"Toni Mustonen (toni.mustonen)",Finnish,78,72,0,0,0,0,0,"2017-09-02 05:34:12"
|
||||||
"Андрій Козицький (andriikozytskyi1108)",Ukrainian,52,52,0,0,1,0,0
|
"Fauz Aladeem (topfauz)",Arabic,76,77,0,0,0,1,0,"2020-02-21 22:46:12"
|
||||||
REMOVED_USER,Italian,51,52,0,2,0,0,0
|
"Michael (quelbs)",German,76,75,0,1,0,0,39,"2020-08-18 07:39:26"
|
||||||
govindap,"Japanese; Hindi",51,114,0,6,1,0,0
|
"Oliver Gronowski (OliverGronowski)",German,70,69,0,5,0,0,0,"2021-05-14 16:37:10"
|
||||||
"Mare Geldenhuys (mare.geldenhuys)",Afrikaans,50,57,0,0,0,0,0
|
RealDonald,Dutch,67,69,0,121,10,0,0,"2017-06-23 20:10:12"
|
||||||
"Peter Williams (williamspete001)",Japanese,49,133,0,1,0,0,3
|
sirekanyan,"Armenian; Russian",66,65,0,0,0,0,0,"2020-04-18 11:32:52"
|
||||||
"Behnood HRazy (behnoodhr)",Persian,49,70,0,0,0,0,0
|
"Константин К. (kocyak1991)",Russian,64,60,0,0,1,2,0,"2018-06-10 13:39:37"
|
||||||
J3ll3nl,Dutch,48,48,0,0,17,1,3
|
"Laura Sophie (laurasophie20)",German,62,67,0,4,0,0,0,"2018-01-06 14:21:24"
|
||||||
"tat bz (Tat_i)",German,48,56,0,55,0,0,27
|
"Alparslan Sakci (sakci)",Turkish,61,55,0,11,0,0,0,"2021-06-10 11:59:22"
|
||||||
vach,Armenian,47,36,0,0,0,0,0
|
raden20,Indonesian,61,62,177,0,1,0,64,"2017-04-09 22:04:23"
|
||||||
andowero,Czech,47,38,0,0,0,0,0
|
"Jan Wojtecki (j4nw)",Polish,58,46,0,0,0,0,26,"2017-11-02 05:42:14"
|
||||||
"Andrew Firnes (Anechan)",Russian,47,47,0,3,0,0,29
|
"Deepak Bharathi (deepakbharathi1994)",Tamil,56,107,0,0,11,4,0,"2017-09-17 08:00:31"
|
||||||
"Coni Ragni (coni2ragnii)",Spanish,46,46,0,0,0,0,0
|
"Peter Williams (williamspete001)",Japanese,55,147,0,2,0,0,3,"2020-01-01 13:17:44"
|
||||||
"Rahul Shishodia (rahul.shishodia.10)",Hindi,46,85,0,6,0,0,0
|
"Андрій Козицький (andriikozytskyi1108)",Ukrainian,52,52,0,0,1,0,0,"2018-10-22 01:45:08"
|
||||||
Cp0204,"Chinese Simplified",45,72,0,0,0,0,0
|
"Nil riera (nilriera2000)",Catalan,52,61,0,1,0,0,0,"2021-06-22 16:37:44"
|
||||||
"cc (cavaz)",Italian,44,41,0,0,0,0,0
|
REMOVED_USER,Italian,51,52,0,2,0,0,0,"2017-08-21 05:15:31"
|
||||||
"Kamil Dziadek (prso94)",Polish,43,39,0,0,2,0,0
|
govindap,"Japanese; Hindi",51,114,0,6,1,0,0,"2020-06-02 20:15:52"
|
||||||
"Boban Jagertraum (boban40)",Czech,43,38,0,2,1,1,0
|
"Mare Geldenhuys (mare.geldenhuys)",Afrikaans,50,57,0,0,0,0,0,"2017-10-20 18:00:14"
|
||||||
andreea.muscalagiu,Romanian,42,52,0,1,0,0,0
|
"Behnood HRazy (behnoodhr)",Persian,49,70,0,0,0,0,0,"2017-11-25 10:57:21"
|
||||||
"Me Me (gentelwom)",Arabic,42,40,0,0,0,0,0
|
"tat bz (Tat_i)",German,48,56,0,55,0,0,27,"2021-03-26 05:12:54"
|
||||||
"Balázs Keresztury (belidzs)",Hungarian,42,41,501,0,7,0,38
|
J3ll3nl,Dutch,48,48,0,0,17,1,3,"2017-03-31 11:56:09"
|
||||||
MStefanov,Bulgarian,41,55,2,0,2,0,2
|
"Andrew Firnes (Anechan)",Russian,47,47,0,3,0,0,29,"2019-09-18 09:51:59"
|
||||||
"Ali Zali (stm19951995)",Persian,40,60,0,0,0,0,0
|
andowero,Czech,47,38,0,0,0,0,0,"2020-01-20 02:29:01"
|
||||||
"Sofia Veijonen (Suklaa) (sofia.veijonen)",Finnish,40,33,0,0,0,0,0
|
vach,Armenian,47,36,0,0,0,0,0,"2020-04-18 16:53:12"
|
||||||
dusanstrgar,Slovenian,39,41,0,0,0,0,0
|
"Rahul Shishodia (rahul.shishodia.10)",Hindi,46,85,0,6,5,1,0,"2018-12-24 22:18:19"
|
||||||
"Limin Lu (liminlu)","Chinese Simplified",39,79,503,0,0,0,39
|
"Coni Ragni (coni2ragnii)",Spanish,46,46,0,0,0,0,0,"2021-02-28 20:18:37"
|
||||||
Anshoe,Tamil,38,65,0,14,0,0,0
|
Cp0204,"Chinese Simplified",45,72,0,0,0,0,0,"2019-08-20 11:04:27"
|
||||||
anasshm,Arabic,37,36,0,9,0,0,0
|
"cc (cavaz)",Italian,44,41,0,0,0,0,0,"2017-04-01 04:21:08"
|
||||||
hrexen,Armenian,37,37,0,0,0,0,0
|
"Boban Jagertraum (boban40)",Czech,43,38,0,2,1,1,0,"2017-03-31 09:39:16"
|
||||||
REMOVED_USER,Swedish,36,33,0,5,1,0,0
|
"Kamil Dziadek (prso94)",Polish,43,39,0,0,2,0,0,"2020-04-06 17:12:06"
|
||||||
"Abdulrahman (D7M)",Arabic,36,39,0,0,0,0,0
|
"Me Me (gentelwom)",Arabic,42,40,0,0,0,0,0,"2020-11-08 20:44:01"
|
||||||
"長谷川知里 (chase0213)",Japanese,34,138,0,13,0,0,24
|
"Ali Elsheikh (aelsheikh1987)",Arabic,42,41,0,0,0,0,0,"2021-06-16 10:17:26"
|
||||||
"Piotr Łuczyński (peterluczynski)",Polish,33,30,0,6,5,0,2
|
"Balázs Keresztury (belidzs)",Hungarian,42,41,501,0,7,0,38,"2017-04-06 02:40:24"
|
||||||
"milad farahani (miladfarmahini90)",Persian,33,44,0,18,0,0,3
|
andreea.muscalagiu,Romanian,42,52,0,1,0,0,0,"2017-10-22 07:19:49"
|
||||||
"Luis E. Perichon (luisperichon)",Spanish,33,40,0,104,0,0,0
|
"Mateusz Duda (MateuszDuda)",Polish,42,42,0,0,0,0,0,"2021-08-17 11:27:11"
|
||||||
andriikozytskyi2625,Ukrainian,31,23,0,0,0,0,0
|
MStefanov,Bulgarian,41,55,2,0,2,0,2,"2017-03-31 16:09:02"
|
||||||
JoeLi,"Chinese Traditional",31,70,0,12,0,0,24
|
"Sofia Veijonen (Suklaa) (sofia.veijonen)",Finnish,40,33,0,0,0,0,0,"2018-03-07 09:24:22"
|
||||||
Moastafa,Arabic,31,25,0,0,0,0,0
|
"Ali Zali (stm19951995)",Persian,40,60,0,0,0,0,0,"2020-03-23 19:57:26"
|
||||||
REMOVED_USER,Russian,31,30,0,2,4,0,3
|
"Limin Lu (liminlu)","Chinese Simplified",39,79,503,0,0,0,39,"2017-03-31 09:49:35"
|
||||||
"hamza gamal (hamzagamal4444)",Arabic,31,28,0,0,0,0,0
|
dusanstrgar,Slovenian,39,41,0,0,0,0,0,"2017-03-31 10:30:28"
|
||||||
yancyn,"Chinese Simplified",30,40,0,0,0,0,0
|
Anshoe,Tamil,38,65,0,14,0,0,0,"2018-01-02 11:06:52"
|
||||||
"비니몬youtube (khj01025276475)",Korean,29,25,0,0,0,0,0
|
anasshm,Arabic,37,36,0,9,0,0,0,"2019-01-27 04:07:22"
|
||||||
"Ruud Schouten (ruudschouten)",Dutch,29,32,0,41,3,0,0
|
hrexen,Armenian,37,37,0,0,0,0,0,"2020-12-09 02:30:34"
|
||||||
"Guillaume Collic (gcollic)",French,26,28,0,126,11,0,0
|
"Abdulrahman (D7M)",Arabic,36,39,0,0,0,0,0,"2020-01-29 18:55:30"
|
||||||
"Niraj Yadav (neverforgetniraj)",Hindi,26,48,0,0,0,0,0
|
"Maria Chushnyakova (maria.ch)",Russian,36,31,0,3,0,0,0,"2021-08-17 03:23:58"
|
||||||
"Radoslaw Biernacki (radoslaw.biernacki)",Polish,26,24,0,8,0,0,1
|
REMOVED_USER,Swedish,36,33,0,5,1,0,0,"2018-09-29 17:47:33"
|
||||||
"Jonny I (jonny99dj)",Italian,26,26,0,5,0,0,0
|
"長谷川知里 (chase0213)",Japanese,34,138,0,13,0,0,24,"2018-12-14 10:52:44"
|
||||||
"Aaron Dalton (Perlkonig)",French,26,25,0,141,1,0,0
|
"Luis E. Perichon (luisperichon)",Spanish,33,40,0,104,0,0,0,"2017-09-04 13:46:06"
|
||||||
"Eddie (eddieattaboy)","Chinese Traditional",25,34,0,1,0,0,0
|
"Piotr Łuczyński (peterluczynski)",Polish,33,30,0,6,5,0,2,"2020-01-29 07:27:40"
|
||||||
Pan_Filuta,Czech,25,21,0,5,4,0,3
|
"milad farahani (miladfarmahini90)",Persian,33,44,0,18,1,0,3,"2017-08-31 16:09:00"
|
||||||
"eduard83 (barbany.eduard)",Catalan,24,25,0,2,0,0,0
|
andriikozytskyi2625,Ukrainian,31,23,0,0,0,0,0,"2019-07-08 00:16:41"
|
||||||
"Ľuboš Čaky (lubos.caky)",Slovak,23,22,0,0,0,0,0
|
Moastafa,Arabic,31,25,0,0,0,0,0,"2020-07-06 11:37:53"
|
||||||
"Caner Başaran (basarancaner)",Turkish,23,21,0,0,20,0,0
|
"hamza gamal (hamzagamal4444)",Arabic,31,28,0,0,0,0,0,"2020-08-03 15:23:34"
|
||||||
hodanli,Turkish,22,26,0,0,1,0,0
|
REMOVED_USER,Russian,31,30,0,2,4,0,3,"2018-12-03 23:55:47"
|
||||||
"Neeraj Verma (verma.neeraj.in)",Hindi,22,37,0,0,1,0,0
|
JoeLi,"Chinese Traditional",31,70,0,12,0,0,24,"2017-06-25 05:32:48"
|
||||||
"Alcarkse (alexis.brusle)",French,21,25,0,7,11,0,0
|
yancyn,"Chinese Simplified",30,40,0,0,0,0,1,"2020-05-18 20:06:03"
|
||||||
"Shashwat (goforgold)",Hindi,20,33,0,0,0,0,0
|
"비니몬youtube (khj01025276475)",Korean,29,25,0,0,0,0,0,"2020-02-09 20:44:35"
|
||||||
"사자솥 (toke1597)",Korean,19,19,0,0,0,0,0
|
"Ruud Schouten (ruudschouten)",Dutch,29,32,0,41,3,0,0,"2017-07-22 17:49:17"
|
||||||
"İsa Eş (IsaEs)",Turkish,19,17,0,0,6,1,0
|
"Aaron Dalton (Perlkonig)",French,26,25,0,141,1,0,0,"2018-01-14 12:58:19"
|
||||||
sheeCesu,French,19,18,0,48,2,0,0
|
"Niraj Yadav (neverforgetniraj)",Hindi,26,48,0,0,0,0,0,"2017-04-11 02:26:50"
|
||||||
can13,Turkish,19,14,0,8,0,0,0
|
"Guillaume Collic (gcollic)",French,26,28,0,126,11,0,0,"2017-05-05 16:13:00"
|
||||||
"Magdalena Urbańczyk (madziia139)",Polish,19,19,0,0,0,0,0
|
"Radoslaw Biernacki (radoslaw.biernacki)",Polish,26,24,0,8,0,0,1,"2020-12-15 17:55:31"
|
||||||
axikman11111,Uyghur,18,19,0,0,0,0,0
|
"Jonny I (jonny99dj)",Italian,26,26,0,5,0,0,0,"2017-10-07 07:35:34"
|
||||||
Adeline31,French,17,20,0,3,0,0,0
|
"Eddie (eddieattaboy)","Chinese Traditional",25,34,0,1,0,0,0,"2020-11-04 21:48:05"
|
||||||
"Hoon Jung (hooni100)",Korean,17,10,0,0,0,0,0
|
Pan_Filuta,Czech,25,21,0,5,4,0,3,"2017-04-29 12:55:14"
|
||||||
"Ceara Lopez (cealopez)",Spanish,17,18,0,0,5,1,0
|
"eduard83 (barbany.eduard)",Catalan,24,25,0,2,0,0,0,"2019-06-26 14:59:47"
|
||||||
takoyakibento,Korean,17,13,0,3,0,0,0
|
"Ľuboš Čaky (lubos.caky)",Slovak,23,22,0,0,0,0,0,"2019-07-02 16:51:44"
|
||||||
bretzel15,German,16,20,0,0,0,0,0
|
"Caner Başaran (basarancaner)",Turkish,23,21,0,0,21,0,0,"2017-04-09 06:34:59"
|
||||||
engineeringforgood,Russian,16,15,0,0,0,0,0
|
hodanli,Turkish,22,26,0,0,1,0,0,"2017-11-03 14:33:41"
|
||||||
"Şamil Ateşoğlu (m.samilatesoglu)",Turkish,16,22,0,11,6,3,0
|
gnu-ewm,Polish,22,23,0,6,0,0,0,"2021-02-24 03:42:01"
|
||||||
DebatablySane,Bulgarian,16,15,0,48,0,0,0
|
"Neeraj Verma (verma.neeraj.in)",Hindi,22,37,0,0,1,0,0,"2018-07-23 07:16:41"
|
||||||
"Bhava Tharini (bhavidanush)",Tamil,15,37,0,0,0,0,0
|
"Alcarkse (alexis.brusle)",French,21,25,0,7,11,0,0,"2017-08-06 09:32:29"
|
||||||
"Anastasia Borchuk (al2.borchuk)",Russian,14,14,0,0,0,0,0
|
olbotta,Italian,20,25,0,2,0,0,0,"2021-06-06 04:22:55"
|
||||||
"EuiHo Hwang (euiho.hwang)",Korean,14,16,0,0,0,0,0
|
"Shashwat (goforgold)",Hindi,20,33,0,0,0,0,0,"2020-05-17 10:34:42"
|
||||||
iamsurajbobade,Hindi,14,30,0,0,0,0,0
|
"Magdalena Urbańczyk (madziia139)",Polish,19,19,0,0,0,0,0,"2017-10-21 03:01:04"
|
||||||
"Zeynep Esen (nezihaesen50)",Turkish,14,13,0,0,0,0,0
|
sheeCesu,French,19,18,0,48,4,0,0,"2017-12-21 17:01:39"
|
||||||
"Fikret Bilici (fikretbilici)",Turkish,14,13,0,0,0,0,0
|
can13,Turkish,19,14,0,8,0,0,0,"2021-01-03 10:39:03"
|
||||||
"Zeeshan Rabbani (Zeera)",Hindi,14,25,0,0,0,0,0
|
"İsa Eş (IsaEs)",Turkish,19,17,0,0,6,1,0,"2017-06-20 07:30:22"
|
||||||
"Sanji Vinsmock (mukanzhanbolat4)",Russian,14,14,0,0,0,0,0
|
"사자솥 (toke1597)",Korean,19,19,0,0,0,0,0,"2020-02-04 13:36:11"
|
||||||
"Nenad Vukotic (vukotic.nenad)","Serbian (Cyrillic)",13,13,0,1,2,6,0
|
axikman11111,Uyghur,18,19,0,0,0,0,0,"2018-10-13 12:25:31"
|
||||||
"Uwe Mönks (schirinowski)",German,13,12,0,0,0,0,0
|
"Hoon Jung (hooni100)",Korean,17,10,0,0,0,0,0,"2021-01-03 02:26:54"
|
||||||
"Dave (xdave)",Hungarian,13,11,0,0,0,0,0
|
"Ceara Lopez (cealopez)",Spanish,17,18,0,0,5,1,0,"2017-08-22 22:56:13"
|
||||||
soura2,Arabic,12,13,0,0,0,0,0
|
takoyakibento,Korean,17,13,0,3,0,0,0,"2020-08-01 08:44:15"
|
||||||
"shreyas (techiespace)",Hindi,12,20,0,0,0,0,0
|
Adeline31,French,17,20,0,3,0,0,0,"2019-12-06 00:00:11"
|
||||||
"Jo Chuang (josephch405)","Chinese Traditional",11,24,0,0,0,0,11
|
engineeringforgood,Russian,16,15,0,0,0,0,16,"2021-01-22 03:32:35"
|
||||||
Vmrc,French,11,12,0,2,0,0,0
|
bretzel15,German,16,20,0,0,0,0,0,"2020-04-06 02:49:14"
|
||||||
"sathvic k (sathvictripleseven)",Telugu,10,17,0,0,0,0,0
|
"Şamil Ateşoğlu (m.samilatesoglu)",Turkish,16,22,0,11,6,3,0,"2017-07-05 18:37:08"
|
||||||
"Edwin van Rooij (edwinvrooij)",Dutch,10,13,0,17,0,0,0
|
DebatablySane,Bulgarian,16,15,0,48,0,0,0,"2017-07-10 15:13:18"
|
||||||
"Brian Camacho (bmcamacho)",Polish,10,11,0,0,1,0,0
|
"Bhava Tharini (bhavidanush)",Tamil,15,37,0,0,0,0,0,"2019-10-09 05:43:11"
|
||||||
"Ahmed Mosaad (ahmed.mosaad2018)",Arabic,10,12,0,6,0,0,0
|
"Zeynep Esen (nezihaesen50)",Turkish,14,13,0,0,0,0,0,"2020-01-28 07:05:15"
|
||||||
"Mihael Wagner (miha.wagner)",Slovenian,10,9,0,7,0,0,0
|
"Fikret Bilici (fikretbilici)",Turkish,14,13,0,0,0,0,0,"2020-06-21 17:16:11"
|
||||||
"Anonymous edgy nerd (yamentaad)",Arabic,10,13,0,1,0,0,0
|
"EuiHo Hwang (euiho.hwang)",Korean,14,16,0,0,0,0,0,"2020-06-23 02:40:01"
|
||||||
"Martin Vostatek (martinvostatek)",Czech,9,8,0,32,2,0,0
|
"Zeeshan Rabbani (Zeera)",Hindi,14,25,0,0,0,0,0,"2020-09-15 11:32:01"
|
||||||
"Sourire Lucide (sourire_lucide)",Russian,9,10,0,0,1,0,0
|
"Faiz Ahamed (faiznewton)",Tamil,14,31,0,0,0,0,0,"2021-05-06 23:06:46"
|
||||||
"Suhaili Hassan (kucingsyg96)",Indonesian,9,10,0,0,0,0,0
|
"Anastasia Borchuk (al2.borchuk)",Russian,14,14,0,0,0,0,0,"2020-04-14 13:22:49"
|
||||||
"Seweryn Piotrowski (Draxxsx)",Polish,9,10,0,0,19,0,0
|
iamsurajbobade,Hindi,14,30,0,0,0,0,0,"2018-05-21 11:23:27"
|
||||||
Rex123,Persian,8,8,0,0,0,0,0
|
"Sanji Vinsmock (mukanzhanbolat4)",Russian,14,14,0,0,0,0,0,"2020-02-18 12:38:54"
|
||||||
"Konstantin (KZhidovinov)",Russian,7,7,0,0,0,0,0
|
"Maro Chr (caprisunglasses)",Greek,14,17,0,0,0,0,0,"2021-08-17 06:53:33"
|
||||||
pkorove,Greek,7,7,0,0,0,0,0
|
"Nenad Vukotic (vukotic.nenad)","Serbian (Cyrillic)",13,13,0,1,2,6,0,"2019-01-31 14:29:15"
|
||||||
ftfoi,Norwegian,7,6,0,0,0,0,0
|
"Uwe Mönks (schirinowski)",German,13,12,0,0,0,0,0,"2021-02-18 04:00:41"
|
||||||
"Андрій Козицький (andriikozytskyi3807)",Ukrainian,7,12,0,2,0,0,0
|
"Dave (xdave)",Hungarian,13,11,0,0,0,0,0,"2020-03-02 20:56:50"
|
||||||
"Vladimir Pavlychev (KeyJoo)",Russian,7,9,0,0,0,0,0
|
soura2,Arabic,12,13,0,0,0,0,0,"2020-01-13 19:23:47"
|
||||||
"Andrey ZaXeLoN (waragaa)",Russian,7,7,0,8,1,0,0
|
"shreyas (techiespace)",Hindi,12,20,0,0,0,0,0,"2018-06-10 01:14:26"
|
||||||
erfan2927,Persian,6,6,0,0,0,0,0
|
"Sonu Sharma (riteetude)",Hindi,11,23,0,0,0,0,0,"2021-05-30 19:38:00"
|
||||||
dragnus,Arabic,6,6,0,1,1,0,0
|
Vmrc,French,11,12,0,2,0,0,0,"2020-11-02 05:35:06"
|
||||||
"Sam (SorodonSorodon)",German,6,6,0,13,0,0,0
|
"Jo Chuang (josephch405)","Chinese Traditional",11,24,0,0,0,0,11,"2017-06-16 20:21:06"
|
||||||
ChloeLiang,Japanese,6,22,0,0,1,0,3
|
"sathvic k (sathvictripleseven)",Telugu,10,17,0,0,0,0,0,"2020-09-11 08:11:32"
|
||||||
"Burak Ceylan (7burakceylan)",Turkish,6,6,0,0,0,0,0
|
"Brian Camacho (bmcamacho)",Polish,10,11,0,0,1,0,0,"2020-08-03 02:27:28"
|
||||||
"닉닉 (seohu9466)",Korean,6,14,0,13,0,0,0
|
"Anonymous edgy nerd (yamentaad)",Arabic,10,13,0,1,0,0,0,"2018-05-06 09:23:57"
|
||||||
"Matthias Joly (joly.matt12)",French,5,8,0,27,1,0,0
|
"Edwin van Rooij (edwinvrooij)",Dutch,10,13,0,17,0,0,0,"2018-11-05 03:59:10"
|
||||||
"Guerra Ivaneth (rossanaiva-04)",Spanish,5,7,0,0,0,0,0
|
"Mihael Wagner (miha.wagner)",Slovenian,10,9,0,7,0,0,0,"2017-10-18 18:26:29"
|
||||||
"Manuel Tassi (Mannivu)",Italian,5,6,0,0,0,0,0
|
"Ahmed Mosaad (ahmed.mosaad2018)",Arabic,10,12,0,6,0,0,0,"2021-02-03 18:45:43"
|
||||||
"Micaela Pighin (micaelapiighin)",Spanish,5,6,0,1,0,0,0
|
"Suhaili Hassan (kucingsyg96)",Indonesian,9,10,0,0,0,0,0,"2018-06-10 11:55:09"
|
||||||
"Дмитрий Хапенков (d.khapenkov)",Russian,5,5,0,6,4,0,2
|
"Martin Vostatek (martinvostatek)",Czech,9,8,0,32,2,0,0,"2019-01-21 13:52:36"
|
||||||
andriikozytskyi2018,Ukrainian,5,5,0,0,0,0,0
|
"Seweryn Piotrowski (Draxxsx)",Polish,9,10,0,0,19,0,0,"2020-01-02 09:55:48"
|
||||||
"Neko123 (emandic11)","Serbian (Cyrillic)",4,4,0,57,0,0,0
|
"Sourire Lucide (sourire_lucide)",Russian,9,10,0,0,1,0,0,"2018-03-22 01:37:55"
|
||||||
marmo,German,4,4,0,0,0,0,0
|
Rex123,Persian,8,8,0,0,0,0,0,"2017-07-01 00:47:42"
|
||||||
"Eli Besirov (elibesirov07)",Turkish,4,4,0,0,0,0,0
|
"Andrey ZaXeLoN (waragaa)",Russian,7,7,0,8,1,0,0,"2017-09-18 21:37:42"
|
||||||
"Lopo Isaac Fernández (rocapata)",Spanish,4,3,0,0,0,0,0
|
"Konstantin (KZhidovinov)",Russian,7,7,0,0,0,0,0,"2020-01-29 13:35:12"
|
||||||
bziuum,Polish,4,4,0,0,0,0,0
|
"Андрій Козицький (andriikozytskyi3807)",Ukrainian,7,12,0,2,0,0,0,"2020-09-26 20:31:56"
|
||||||
"Thoum Ptrgnt (thomas.petrignet)",French,3,3,0,2,0,3,0
|
ftfoi,Norwegian,7,6,0,0,0,0,0,"2020-04-11 20:42:35"
|
||||||
"Vagner Roberto (vagner.trompete)","Portuguese, Brazilian",3,3,0,0,0,0,0
|
"Vladimir Pavlychev (KeyJoo)",Russian,7,9,0,0,0,0,0,"2017-12-18 02:46:56"
|
||||||
"Unnie Here (Carb)",Hindi,3,8,0,0,0,0,0
|
pkorove,Greek,7,7,0,0,0,0,0,"2020-03-07 11:36:12"
|
||||||
carsten_kafke,German,3,3,0,43,0,0,3
|
erfan2927,Persian,6,6,0,0,0,0,0,"2018-04-09 02:12:44"
|
||||||
Magidxz,Arabic,3,3,0,0,0,0,0
|
"Burak Ceylan (7burakceylan)",Turkish,6,6,0,0,0,0,0,"2018-05-20 17:24:19"
|
||||||
atomjani,Hungarian,3,3,0,0,0,0,0
|
"Sam (SorodonSorodon)",German,6,6,0,13,0,0,0,"2017-04-14 11:09:27"
|
||||||
"Péter Bernát (bernatp)",Hungarian,3,2,0,0,0,0,0
|
"닉닉 (seohu9466)",Korean,6,14,0,13,0,0,0,"2017-10-09 23:08:15"
|
||||||
"Igor Piskun (i_piskun)",Ukrainian,3,3,0,0,0,0,0
|
"Sarita Cajas (sarayanacajas)",Spanish,6,4,0,0,1,0,0,"2021-05-14 14:27:59"
|
||||||
"joabe gabriel (joabegabrielcma1)","Portuguese, Brazilian",3,4,0,0,0,0,0
|
ChloeLiang,Japanese,6,22,0,0,1,0,3,"2017-08-08 05:02:59"
|
||||||
"Oleg Kogut (kogut_oleg)",Ukrainian,3,3,0,0,0,0,0
|
"Manuel Tassi (Mannivu)",Italian,5,6,0,0,0,0,0,"2021-01-03 11:00:33"
|
||||||
"Andrea Bianchi (andreawhite1597)",Italian,3,1,0,1,0,0,0
|
"Tomáš Hrabáček (Hrabyyy)",Czech,5,3,0,0,0,0,0,"2021-05-27 11:58:11"
|
||||||
"Gabriel Cavalcante (gabrielc.alves14)","Portuguese, Brazilian",3,4,0,0,0,0,0
|
"Guerra Ivaneth (rossanaiva-04)",Spanish,5,7,0,0,0,0,0,"2019-02-03 16:48:59"
|
||||||
REMOVED_USER,"Portuguese, Brazilian",3,4,0,0,0,0,0
|
"Дмитрий Хапенков (d.khapenkov)",Russian,5,5,0,6,4,0,2,"2018-01-06 23:00:43"
|
||||||
"Martin Zimdahl (zimdahlmartin)",Swedish,3,2,0,0,1,0,3
|
"Matthias Joly (joly.matt12)",French,5,8,0,27,1,0,0,"2017-08-28 09:53:59"
|
||||||
"Hiohana Rilary (hiohanarilary)","Portuguese, Brazilian",3,4,0,0,0,0,0
|
"Micaela Pighin (micaelapiighin)",Spanish,5,6,0,1,0,0,0,"2019-10-09 23:32:42"
|
||||||
"Sarath S (CyberShark)",Tamil,3,7,0,0,0,0,0
|
andriikozytskyi2018,Ukrainian,5,5,0,0,0,0,0,"2017-09-03 05:24:43"
|
||||||
"Cláudio Bernardo (claudiobernardo.ti)","Portuguese, Brazilian",3,4,0,1,0,0,0
|
marmo,German,4,4,0,0,0,0,0,"2021-01-13 01:16:35"
|
||||||
"hesamiranii (esam.matouri)",Persian,2,2,0,0,0,0,0
|
"Eli Besirov (elibesirov07)",Turkish,4,4,0,0,0,0,0,"2019-03-25 07:12:34"
|
||||||
"조화정 (yunjoo337)",Korean,2,2,0,0,0,0,0
|
"Lopo Isaac Fernández (rocapata)",Spanish,4,3,0,0,0,0,0,"2018-09-20 11:46:22"
|
||||||
"Walid Baazia (walidbaazia2005)",Arabic,2,1,0,0,0,0,0
|
bziuum,Polish,4,4,0,0,0,0,0,"2020-09-01 09:08:01"
|
||||||
LNDDYL,"Chinese Traditional",2,4,0,0,0,0,2
|
"Neko123 (emandic11)","Serbian (Cyrillic)",4,4,0,57,0,0,0,"2021-04-21 15:33:29"
|
||||||
REMOVED_USER,Ukrainian,2,2,0,0,0,0,0
|
Magidxz,Arabic,3,3,0,0,0,0,0,"2021-01-05 05:02:54"
|
||||||
"fatemeh s (fargolseifoori3)",Persian,2,2,0,0,0,0,0
|
"mohammadali barati (mabaraty)",Persian,3,3,0,0,0,0,0,"2021-07-10 05:54:44"
|
||||||
"أم محمد تقي (souadboudia19)",Arabic,2,2,0,0,0,0,0
|
"Sarath S (CyberShark)",Tamil,3,7,0,0,0,0,0,"2020-08-27 22:43:16"
|
||||||
"Alex Stein (diefaust1993)",Russian,2,2,0,4,4,0,2
|
"Unnie Here (Carb)",Hindi,3,8,0,0,0,0,0,"2020-03-18 23:34:35"
|
||||||
"Danial Agh (danialagh)",Persian,2,3,0,0,0,0,0
|
REMOVED_USER,"Portuguese, Brazilian",3,4,0,0,0,0,0,"2018-11-18 09:02:37"
|
||||||
omerfarukbas,Turkish,2,3,0,19,2,0,0
|
"Thoum Ptrgnt (thomas.petrignet)",French,3,3,0,2,0,3,0,"2017-09-23 19:25:52"
|
||||||
"Valerij D (vala.dobler)",German,2,2,0,0,0,0,0
|
"Oleg Kogut (kogut_oleg)",Ukrainian,3,3,0,0,0,0,0,"2018-12-28 14:31:02"
|
||||||
"Naveen jai krishna (njsbpolymer1)",Tamil,2,5,0,0,0,0,0
|
carsten_kafke,German,3,3,0,43,0,0,3,"2017-10-27 13:27:47"
|
||||||
"Balthazar Aubard (Balatzar)",French,2,5,0,0,1,0,0
|
"Vagner Roberto (vagner.trompete)","Portuguese, Brazilian",3,3,0,0,0,0,0,"2017-12-30 17:54:26"
|
||||||
"FAy FAy (fayfayfay52)","Chinese Traditional",2,5,0,0,0,0,0
|
"Igor Piskun (i_piskun)",Ukrainian,3,3,0,0,0,0,0,"2018-01-19 15:20:27"
|
||||||
Soroor_SI,Persian,2,2,0,0,0,0,0
|
"Andrea Bianchi (andreawhite1597)",Italian,3,1,0,1,0,0,0,"2018-01-21 17:45:48"
|
||||||
chavs1997,Russian,2,2,0,9,0,0,0
|
"Cláudio Bernardo (claudiobernardo.ti)","Portuguese, Brazilian",3,4,0,1,0,0,0,"2019-01-08 14:41:10"
|
||||||
amei,"Portuguese, Brazilian",2,2,0,0,0,0,0
|
"Hiohana Rilary (hiohanarilary)","Portuguese, Brazilian",3,4,0,0,0,0,0,"2019-07-31 20:42:20"
|
||||||
"Ilyas Fekhar (il47yas)",Arabic,2,2,0,0,0,0,0
|
"joabe gabriel (joabegabrielcma1)","Portuguese, Brazilian",3,4,0,0,0,0,0,"2018-08-21 09:08:59"
|
||||||
"Ali Zaida (alizaeda92)",Arabic,2,2,0,0,0,0,0
|
"Péter Bernát (bernatp)",Hungarian,3,2,0,0,0,0,0,"2019-11-30 15:50:33"
|
||||||
gnu-ewm,Polish,1,1,0,6,0,0,0
|
"Martin Zimdahl (zimdahlmartin)",Swedish,3,2,0,0,1,0,3,"2018-09-15 04:39:22"
|
||||||
"Luca Gori (grolcu)",Italian,1,2,0,0,0,0,0
|
"Gabriel Cavalcante (gabrielc.alves14)","Portuguese, Brazilian",3,4,0,0,0,0,0,"2018-08-06 22:24:54"
|
||||||
"Alan Jeon (skyisle)",Korean,1,2,0,8,0,0,0
|
atomjani,Hungarian,3,3,0,0,0,0,0,"2019-01-19 00:49:25"
|
||||||
iSoron2,"Portuguese, Brazilian",1,1,0,0,0,0,0
|
"أم محمد تقي (souadboudia19)",Arabic,2,2,0,0,0,0,0,"2020-06-13 15:24:17"
|
||||||
"Anastasiia Bondarenko (nastasya.bondarenko.97)",Russian,1,1,0,0,0,0,0
|
"FAy FAy (fayfayfay52)","Chinese Traditional",2,5,0,0,0,0,0,"2017-10-06 08:53:21"
|
||||||
"Patrick Pimenta (trickap1)","Portuguese, Brazilian",1,1,0,0,0,0,0
|
chavs1997,Russian,2,2,0,9,0,0,0,"2018-05-18 16:58:19"
|
||||||
axd,Spanish,1,1,0,15,0,0,0
|
Soroor_SI,Persian,2,2,0,0,0,0,0,"2018-06-10 06:28:27"
|
||||||
jonesses,German,1,1,0,1,0,0,1
|
"Ilyas Fekhar (il47yas)",Arabic,2,2,0,0,0,0,0,"2018-04-17 22:00:41"
|
||||||
"박찌 (perpact20)",Korean,1,1,0,0,0,0,0
|
"hesamiranii (esam.matouri)",Persian,2,2,0,0,0,0,0,"2018-09-22 16:33:36"
|
||||||
"Kan Black (kanblack.va)",Vietnamese,1,2,0,0,0,1,0
|
"fatemeh s (fargolseifoori3)",Persian,2,2,0,0,0,0,0,"2019-01-31 12:06:57"
|
||||||
REMOVED_USER,Russian,1,2,0,6,1,0,1
|
amei,"Portuguese, Brazilian",2,2,0,0,0,0,0,"2018-04-19 19:42:28"
|
||||||
"Wibi Cahyo (wbcahyoh)",Indonesian,1,3,0,0,0,0,0
|
"Naveen jai krishna (njsbpolymer1)",Tamil,2,5,0,0,0,0,0,"2020-01-10 14:19:41"
|
||||||
"Anton (tT0NG)","Chinese Traditional",1,2,0,0,0,0,1
|
"Danial Agh (danialagh)",Persian,2,3,0,0,0,0,0,"2019-03-30 13:24:16"
|
||||||
"Maria Fefelova (mashafefel)",Russian,1,1,0,0,0,0,0
|
"Walid Baazia (walidbaazia2005)",Arabic,2,1,0,0,0,0,0,"2021-01-27 12:47:34"
|
||||||
Itch,,0,0,0,0,0,0,0
|
"Ali Zaida (alizaeda92)",Arabic,2,2,0,0,0,0,0,"2019-12-01 11:47:00"
|
||||||
"Muhammet Furkan ALMACI (furkan.almaci)",Turkish,0,0,0,1,0,0,0
|
LNDDYL,"Chinese Traditional",2,4,0,0,0,0,2,"2018-04-22 04:00:19"
|
||||||
Sandhu564.,,0,0,0,0,0,0,0
|
"Ño Bí Tã (pt614553)",Arabic,2,8,0,1,0,0,0,"2021-05-22 20:41:01"
|
||||||
"Quentin Hibon (hiq)",,0,0,0,0,0,0,0
|
"Judith Ayala (Azul1612)",Spanish,2,1,0,0,0,1,0,"2021-05-18 17:07:19"
|
||||||
mushin,,0,0,0,0,0,0,0
|
REMOVED_USER,Ukrainian,2,2,0,0,0,0,0,"2017-06-15 12:24:44"
|
||||||
"Balaji Jayaraman (jkbalaji1103)",,0,0,0,0,0,0,0
|
"Valerij D (vala.dobler)",German,2,2,0,0,0,0,0,"2018-09-22 09:38:27"
|
||||||
"Wellington Ribeiro (wellington.rib)",,0,0,0,0,0,0,0
|
"Alex Stein (diefaust1993)",Russian,2,2,0,4,4,0,2,"2017-07-13 06:56:17"
|
||||||
"Javid IRAN (twitteriran98)",Persian,0,0,0,1,0,0,0
|
"조화정 (yunjoo337)",Korean,2,2,0,0,0,0,0,"2019-06-16 22:25:31"
|
||||||
"박인호 (wphestiraid)",Korean,0,0,0,2,0,0,0
|
omerfarukbas,Turkish,2,3,0,19,2,0,0,"2017-08-14 16:10:35"
|
||||||
"Ahmed Nazir (ahmednazir333)",,0,0,0,0,0,0,0
|
"Balthazar Aubard (Balatzar)",French,2,5,0,0,1,0,0,"2017-09-23 01:42:57"
|
||||||
dimateos,,0,0,0,0,0,0,0
|
"Luca Gori (grolcu)",Italian,1,2,0,0,0,0,0,"2020-09-26 23:26:15"
|
||||||
AhmedDz,Arabic,0,0,0,1,0,0,0
|
axd,Spanish,1,1,0,15,0,0,0,"2017-09-12 05:48:51"
|
||||||
"reza golestanzadeh (reza.golestanzadeh)",Persian,0,0,0,1,0,0,0
|
iSoron2,"Portuguese, Brazilian",1,1,0,0,0,0,0,"2017-03-18 17:56:29"
|
||||||
"Petros Bleyan (coolbleyan)",Russian,0,0,0,14,0,0,0
|
REMOVED_USER,Russian,1,2,0,6,1,0,1,"2019-12-26 05:37:01"
|
||||||
Kamalakannan,,0,0,0,0,0,0,0
|
"Wibi Cahyo (wbcahyoh)",Indonesian,1,3,0,0,0,0,0,"2017-12-14 06:35:58"
|
||||||
"Dagna Q (dagnaq)",,0,0,0,0,0,0,0
|
jonesses,German,1,1,0,1,0,0,1,"2021-01-01 08:03:18"
|
||||||
"Thomas Orlita (Thomas995)",Czech,0,0,0,1,0,0,0
|
"Anton (tT0NG)","Chinese Traditional",1,2,0,0,0,0,1,"2017-07-06 14:18:39"
|
||||||
Fazy1380,,0,0,0,0,0,0,0
|
"박찌 (perpact20)",Korean,1,1,0,0,0,0,0,"2018-02-10 10:11:44"
|
||||||
"Rivo Zängov (Eraser)",,0,0,0,0,0,0,0
|
"Alan Jeon (skyisle)",Korean,1,2,0,8,0,0,0,"2018-01-09 10:46:00"
|
||||||
T-v-Gerwen,Dutch,0,0,0,47,0,0,0
|
"Maria Fefelova (mashafefel)",Russian,1,1,0,0,0,0,0,"2019-05-18 02:03:56"
|
||||||
"عبد الناصر سعيد الثبيتي (asaeed)",,0,0,0,0,0,0,0
|
"Anastasiia Bondarenko (nastasya.bondarenko.97)",Russian,1,1,0,0,0,0,0,"2019-06-07 17:43:08"
|
||||||
"Edmunds Edmundam (edmundam)",,0,0,0,0,0,0,0
|
"Kan Black (kanblack.va)",Vietnamese,1,2,0,0,0,1,0,"2019-01-15 03:50:10"
|
||||||
"vi ve (VimalV)",,0,0,0,0,0,0,0
|
"Patrick Pimenta (trickap1)","Portuguese, Brazilian",1,1,0,0,0,0,0,"2018-12-01 14:31:21"
|
||||||
"LeMeD (LeMeS)",French,0,0,0,2,0,0,0
|
"Dagna Q (dagnaq)",,0,0,0,0,0,0,0,"2017-08-06 01:42:52"
|
||||||
"Éjbãss Übbeî (littlebittlebottle)",Norwegian,0,0,0,152,0,0,0
|
Kamalakannan,,0,0,0,0,0,0,0,"2017-05-14 11:40:23"
|
||||||
EmanAmini,,0,0,0,0,0,0,0
|
"Éjbãss Übbeî (littlebittlebottle)",Norwegian,0,0,0,152,0,0,0,"2017-07-05 21:12:02"
|
||||||
AnggaRifandi,,0,0,0,0,0,0,0
|
"Равиль Мифтахов (ravilmif47)",Russian,0,0,0,1,0,0,0,"2019-08-12 21:58:30"
|
||||||
"Mateusz Teteruk (mttet)",Polish,0,0,0,1,0,0,0
|
sanyoniket,,0,0,0,0,0,0,0,"2019-07-23 12:58:40"
|
||||||
"Lori Amico (lavodkaclyde2323)",Italian,0,0,0,1,0,0,0
|
REMOVED_USER,,0,0,0,0,0,0,0,"2020-02-01 03:47:48"
|
||||||
"Florian Stuhlmann (stuhlmann)",German,0,0,0,10,0,0,0
|
"vi ve (VimalV)",,0,0,0,0,0,0,0,"2021-02-08 02:35:45"
|
||||||
"Yasin Okumus (lacivert)",Turkish,0,0,0,1,0,0,0
|
"George Merkulov (george142.emarket)",Russian,0,0,0,11,0,0,0,"2019-06-09 19:47:02"
|
||||||
NairaDNV,Spanish,0,0,0,9,0,0,0
|
"Yasin Okumus (lacivert)",Turkish,0,0,0,1,0,0,0,"2018-02-07 04:13:51"
|
||||||
REMOVED_USER,,0,0,0,0,0,0,0
|
"Petros Bleyan (coolbleyan)",Russian,0,0,0,14,0,0,0,"2017-08-18 18:37:18"
|
||||||
farbod66,Persian,0,0,0,1,0,0,0
|
"LeMeD (LeMeS)",French,0,0,0,2,0,0,0,"2021-02-06 15:35:00"
|
||||||
Raulbertassi,,0,0,0,0,0,0,0
|
ava_rfie,Persian,0,0,0,1,0,0,0,"2019-06-09 16:19:24"
|
||||||
"Равиль Мифтахов (ravilmif47)",Russian,0,0,0,1,0,0,0
|
"Mateusz Teteruk (mttet)",Polish,0,0,0,1,0,0,0,"2021-01-23 13:09:59"
|
||||||
"Pumpith Ungsupanit (pumpithu)",,0,0,0,0,0,0,0
|
EwanB,,0,0,0,0,0,0,0,"2019-11-19 10:04:38"
|
||||||
"Nat Fomicheva (natac)",Russian,0,0,0,3,0,0,0
|
Fazy1380,,0,0,0,0,0,0,0,"2021-04-10 11:02:53"
|
||||||
EwanB,,0,0,0,0,0,0,0
|
"Lori Amico (lavodkaclyde2323)",Italian,0,0,0,1,0,0,0,"2017-04-09 10:08:13"
|
||||||
ava_rfie,Persian,0,0,0,1,0,0,0
|
"Florian Stuhlmann (stuhlmann)",German,0,0,0,10,0,0,0,"2017-04-15 04:04:00"
|
||||||
"George Merkulov (george142.emarket)",Russian,0,0,0,11,0,0,0
|
"عبد الناصر سعيد الثبيتي (asaeed)",,0,0,0,0,0,0,0,"2018-03-13 02:09:35"
|
||||||
REMOVED_USER,,0,0,0,0,0,0,0
|
"Rivo Zängov (Eraser)",,0,0,0,0,0,0,0,"2020-10-13 04:38:26"
|
||||||
sanyoniket,,0,0,0,0,0,0,0
|
Hayder21,,0,0,0,0,0,0,0,"2019-12-31 10:56:24"
|
||||||
"Shuvashish Sahoo (shuvashish76)",,0,0,0,0,0,0,0
|
T-v-Gerwen,Dutch,0,0,0,47,0,0,0,"2018-03-02 10:26:33"
|
||||||
martyaberger,,0,0,0,0,0,0,0
|
"Eduard Boboc (edi.boboc33)",Romanian,0,0,0,4,0,0,0,"2019-12-16 09:08:39"
|
||||||
"Карлен Шаухаев (KarlenShaukhaev)",,0,0,0,0,0,0,0
|
"Samuel Przeździęk (samek22)",Polish,0,0,0,1,0,0,0,"2021-08-01 00:49:01"
|
||||||
"Elmo (oberknecht)",,0,0,0,0,0,0,0
|
"Saiprasath B (Saiprasath)",,0,0,0,0,0,0,0,"2021-07-11 11:10:41"
|
||||||
Irsgram,Russian,0,0,0,1,0,0,0
|
shuvo786,,0,0,0,0,0,0,0,"2019-11-13 00:18:12"
|
||||||
"Matus Zdansky (matuszdansky)",,0,0,0,0,0,0,0
|
"Edmunds Edmundam (edmundam)",,0,0,0,0,0,0,0,"2020-06-01 14:18:18"
|
||||||
shuvo786,,0,0,0,0,0,0,0
|
Itch,,0,0,0,0,0,0,0,"2017-10-16 09:18:42"
|
||||||
"Eduard Boboc (edi.boboc33)",Romanian,0,0,0,4,0,0,0
|
"Manny Farsangy (manifarsangi)",Persian,0,0,0,12,0,0,0,"2021-08-10 05:32:28"
|
||||||
Hayder21,,0,0,0,0,0,0,0
|
"Matus Zdansky (matuszdansky)",,0,0,0,0,0,0,0,"2019-10-20 13:52:24"
|
||||||
AsadullahIlyas,,0,0,0,0,0,0,0
|
"Thomas Orlita (Thomas995)",Czech,0,0,0,1,0,0,0,"2017-12-24 04:08:27"
|
||||||
"Никита Карамов (nikita.karamoff)",Russian,0,0,0,10,0,0,0
|
Irsgram,Russian,0,0,0,1,0,0,0,"2019-09-30 16:42:20"
|
||||||
"Katherine Alexandra Flórez Ramírez (katherine.florez12)",Spanish,0,0,0,46,0,0,0
|
EmanAmini,,0,0,0,0,0,0,0,"2017-03-31 13:27:43"
|
||||||
mdrobulis,,0,0,0,0,0,0,0
|
mushin,,0,0,0,0,0,0,0,"2020-02-02 04:08:05"
|
||||||
"Eliška Roubalová (roubaeli)",Czech,0,0,0,6,0,0,0
|
"Elmo (oberknecht)",,0,0,0,0,0,0,0,"2020-04-16 08:45:50"
|
||||||
droidahmed,Arabic,0,0,0,7,0,0,0
|
AnggaRifandi,,0,0,0,0,0,0,0,"2017-03-31 19:28:35"
|
||||||
"Arjun K. (arjunkdot)",,0,0,0,0,0,0,0
|
"darkkingredian (rediancool)",,0,0,0,0,0,0,0,"2021-07-27 16:04:32"
|
||||||
"Sarah BCNN (fsarahboucenna)",French,0,0,0,16,0,0,0
|
"Sri Harsha Bhogi (sriharshabhogi)",,0,0,0,0,0,0,0,"2018-09-02 05:31:53"
|
||||||
REMOVED_USER,Czech,0,0,0,18,0,0,0
|
"Nat Fomicheva (natac)",Russian,0,0,0,3,0,0,0,"2019-01-25 14:35:02"
|
||||||
BongTran,Vietnamese,0,0,0,2,0,0,0
|
mdrobulis,,0,0,0,0,0,0,0,"2018-05-24 01:40:42"
|
||||||
"Arttu Ylhävuori (arttu.ylhavuori)",,0,0,0,0,0,0,0
|
"Sarah BCNN (fsarahboucenna)",French,0,0,0,16,0,0,0,"2018-02-11 11:07:36"
|
||||||
valney.faria,"Portuguese, Brazilian",0,0,0,1,0,0,0
|
"Arjun K. (arjunkdot)",,0,0,0,0,0,0,0,"2020-09-20 11:16:18"
|
||||||
REMOVED_USER,,0,0,0,0,0,0,0
|
REMOVED_USER,Czech,0,0,0,18,0,0,0,"2018-03-27 06:19:52"
|
||||||
rooban23,,0,0,0,0,0,0,0
|
martyaberger,,0,0,0,0,0,0,0,"2019-01-01 18:48:08"
|
||||||
REMOVED_USER,,0,0,0,0,0,0,0
|
BongTran,Vietnamese,0,0,0,2,0,0,0,"2018-04-24 05:16:07"
|
||||||
"Алтынбек Наурызғали (altinbeknaurizgali)",Russian,0,0,0,1,0,0,0
|
"Arttu Ylhävuori (arttu.ylhavuori)",,0,0,0,0,0,0,0,"2019-07-24 15:03:42"
|
||||||
"Sri Harsha Bhogi (sriharshabhogi)",,0,0,0,0,0,0,0
|
"Никита Карамов (nikita.karamoff)",Russian,0,0,0,10,0,0,0,"2018-10-29 03:57:21"
|
||||||
"Ahnaf Tajwar (atn4404)",,0,0,0,0,0,0,0
|
rooban23,,0,0,0,0,0,0,0,"2020-09-15 11:49:14"
|
||||||
Elham1361,,0,0,0,0,0,0,0
|
"Eliška Roubalová (roubaeli)",Czech,0,0,0,6,0,0,0,"2019-12-31 12:47:29"
|
||||||
dongchen.yue,German,0,0,0,4,0,0,0
|
valney.faria,"Portuguese, Brazilian",0,0,0,1,0,0,0,"2020-02-02 14:45:02"
|
||||||
philfr49,French,0,0,0,2,0,0,0
|
"Алтынбек Наурызғали (altinbeknaurizgali)",Russian,0,0,0,1,0,0,0,"2020-08-12 13:03:49"
|
||||||
|
REMOVED_USER,,0,0,0,0,0,0,0,"2018-10-27 15:34:36"
|
||||||
|
REMOVED_USER,,0,0,0,0,0,0,0,"2018-08-24 00:17:43"
|
||||||
|
Elham1361,,0,0,0,0,0,0,0,"2018-10-27 12:01:06"
|
||||||
|
dongchen.yue,German,0,0,0,4,0,0,0,"2020-09-12 15:05:59"
|
||||||
|
"Ahnaf Tajwar (atn4404)",,0,0,0,0,0,0,0,"2018-10-16 11:13:30"
|
||||||
|
AsadullahIlyas,,0,0,0,0,0,0,0,"2019-01-04 06:14:15"
|
||||||
|
droidahmed,Arabic,0,0,0,7,0,0,0,"2018-01-31 02:18:49"
|
||||||
|
philfr49,French,0,0,0,2,0,0,0,"2018-09-03 14:20:32"
|
||||||
|
"Ahmed Nazir (ahmednazir333)",,0,0,0,0,0,0,0,"2018-05-06 12:10:27"
|
||||||
|
"Balaji Jayaraman (jkbalaji1103)",,0,0,0,0,0,0,0,"2017-10-30 22:12:27"
|
||||||
|
"Wellington Ribeiro (wellington.rib)",,0,0,0,0,0,0,0,"2017-11-16 07:32:25"
|
||||||
|
"Javid IRAN (twitteriran98)",Persian,0,0,0,1,0,0,0,"2017-11-25 16:47:25"
|
||||||
|
"박인호 (wphestiraid)",Korean,0,0,0,2,0,0,0,"2018-01-05 00:33:14"
|
||||||
|
"Pumpith Ungsupanit (pumpithu)",,0,0,0,0,0,0,0,"2019-01-19 23:47:57"
|
||||||
|
Sandhu564.,,0,0,0,0,0,0,0,"2020-12-14 01:27:45"
|
||||||
|
"Quentin Hibon (hiq)",,0,0,0,0,0,0,0,"2021-02-07 16:39:31"
|
||||||
|
AhmedDz,Arabic,0,0,0,1,0,0,0,"2017-12-31 10:12:31"
|
||||||
|
"Shuvashish Sahoo (shuvashish76)",,0,0,0,0,0,0,0,"2020-09-17 09:10:09"
|
||||||
|
REMOVED_USER,,0,0,0,0,0,0,0,"2018-01-05 16:56:12"
|
||||||
|
NairaDNV,Spanish,0,0,0,9,0,0,0,"2018-01-05 19:10:33"
|
||||||
|
Raulbertassi,,0,0,0,0,0,0,0,"2018-01-07 17:23:18"
|
||||||
|
"Карлен Шаухаев (KarlenShaukhaev)",,0,0,0,0,0,0,0,"2020-04-27 08:53:49"
|
||||||
|
dimateos,,0,0,0,0,0,0,0,"2021-01-10 06:29:52"
|
||||||
|
"Katherine Alexandra Flórez Ramírez (katherine.florez12)",Spanish,0,0,0,46,0,0,0,"2018-01-20 02:18:32"
|
||||||
|
"reza golestanzadeh (reza.golestanzadeh)",Persian,0,0,0,1,0,0,0,"2020-10-21 12:07:20"
|
||||||
|
farbod66,Persian,0,0,0,1,0,0,0,"2018-01-20 11:04:23"
|
||||||
|
"Muhammet Furkan ALMACI (furkan.almaci)",Turkish,0,0,0,1,0,0,0,"2017-10-29 13:44:56"
|
||||||
|
|||||||
|
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
*
|
*
|
||||||
@@ -20,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.github.triplet.play") version "3.2.0"
|
id("com.github.triplet.play") version "3.7.0"
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
id("org.jetbrains.kotlin.android")
|
id("org.jetbrains.kotlin.android")
|
||||||
id("org.jetbrains.kotlin.kapt")
|
id("org.jetbrains.kotlin.kapt")
|
||||||
@@ -34,15 +32,15 @@ tasks.compileLint {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
compileSdkVersion(30)
|
compileSdk = 31
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
versionCode(20002)
|
versionCode = 20003
|
||||||
versionName("2.0.2")
|
versionName = "2.0.3"
|
||||||
minSdkVersion(23)
|
minSdk = 23
|
||||||
targetSdkVersion(30)
|
targetSdk = 31
|
||||||
applicationId("org.isoron.uhabits")
|
applicationId = "org.isoron.uhabits"
|
||||||
testInstrumentationRunner("androidx.test.runner.AndroidJUnitRunner")
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
@@ -58,7 +56,7 @@ android {
|
|||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
getByName("release") {
|
getByName("release") {
|
||||||
minifyEnabled(true)
|
isMinifyEnabled = true
|
||||||
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.txt")
|
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.txt")
|
||||||
if (signingConfigs.findByName("release") != null) {
|
if (signingConfigs.findByName("release") != null) {
|
||||||
signingConfig = signingConfigs.getByName("release")
|
signingConfig = signingConfigs.getByName("release")
|
||||||
@@ -70,7 +68,7 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lintOptions {
|
lint {
|
||||||
isCheckReleaseBuilds = false
|
isCheckReleaseBuilds = false
|
||||||
isAbortOnError = false
|
isAbortOnError = false
|
||||||
disable("GoogleAppIndexingWarning")
|
disable("GoogleAppIndexingWarning")
|
||||||
@@ -88,29 +86,29 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
val daggerVersion = "2.34.1"
|
val daggerVersion = "2.43.1"
|
||||||
val kotlinVersion = "1.4.32"
|
val kotlinVersion = "1.7.0"
|
||||||
val kxCoroutinesVersion = "1.4.2"
|
val kxCoroutinesVersion = "1.6.4"
|
||||||
val ktorVersion = "1.5.3"
|
val ktorVersion = "1.6.8"
|
||||||
val espressoVersion = "3.3.0"
|
val espressoVersion = "3.4.0"
|
||||||
|
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-contrib:$espressoVersion")
|
androidTestImplementation("androidx.test.espresso:espresso-contrib:$espressoVersion")
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:$espressoVersion")
|
androidTestImplementation("androidx.test.espresso:espresso-core:$espressoVersion")
|
||||||
androidTestImplementation("com.google.dagger:dagger:$daggerVersion")
|
androidTestImplementation("com.google.dagger:dagger:$daggerVersion")
|
||||||
androidTestImplementation("com.linkedin.dexmaker:dexmaker-mockito:2.28.1")
|
androidTestImplementation("com.linkedin.dexmaker:dexmaker-mockito:2.28.3")
|
||||||
androidTestImplementation("io.ktor:ktor-client-mock:$ktorVersion")
|
androidTestImplementation("io.ktor:ktor-client-mock:$ktorVersion")
|
||||||
androidTestImplementation("io.ktor:ktor-jackson:$ktorVersion")
|
androidTestImplementation("io.ktor:ktor-jackson:$ktorVersion")
|
||||||
androidTestImplementation("androidx.annotation:annotation:1.2.0")
|
androidTestImplementation("androidx.annotation:annotation:1.4.0")
|
||||||
androidTestImplementation("androidx.test.ext:junit:1.1.2")
|
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
||||||
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
|
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
|
||||||
androidTestImplementation("androidx.test:rules:1.3.0")
|
androidTestImplementation("androidx.test:rules:1.4.0")
|
||||||
androidTestImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
|
androidTestImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
|
||||||
compileOnly("javax.annotation:jsr250-api:1.0")
|
compileOnly("javax.annotation:jsr250-api:1.0")
|
||||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
|
||||||
implementation("com.github.paolorotolo:appintro:3.4.0")
|
implementation("com.github.AppIntro:AppIntro:6.2.0")
|
||||||
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
||||||
implementation("com.google.dagger:dagger:$daggerVersion")
|
implementation("com.google.dagger:dagger:$daggerVersion")
|
||||||
implementation("com.google.guava:guava:30.1.1-android")
|
implementation("com.google.guava:guava:31.1-android")
|
||||||
implementation("io.ktor:ktor-client-android:$ktorVersion")
|
implementation("io.ktor:ktor-client-android:$ktorVersion")
|
||||||
implementation("io.ktor:ktor-client-core:$ktorVersion")
|
implementation("io.ktor:ktor-client-core:$ktorVersion")
|
||||||
implementation("io.ktor:ktor-client-jackson:$ktorVersion")
|
implementation("io.ktor:ktor-client-jackson:$ktorVersion")
|
||||||
@@ -118,11 +116,11 @@ dependencies {
|
|||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$kxCoroutinesVersion")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$kxCoroutinesVersion")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kxCoroutinesVersion")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kxCoroutinesVersion")
|
||||||
implementation("androidx.appcompat:appcompat:1.2.0")
|
implementation("androidx.appcompat:appcompat:1.4.2")
|
||||||
implementation("androidx.legacy:legacy-preference-v14:1.0.0")
|
implementation("androidx.legacy:legacy-preference-v14:1.0.0")
|
||||||
implementation("androidx.legacy:legacy-support-v4:1.0.0")
|
implementation("androidx.legacy:legacy-support-v4:1.0.0")
|
||||||
implementation("com.google.android.material:material:1.3.0")
|
implementation("com.google.android.material:material:1.6.1")
|
||||||
implementation("com.opencsv:opencsv:5.4")
|
implementation("com.opencsv:opencsv:5.6")
|
||||||
implementation(project(":uhabits-core"))
|
implementation(project(":uhabits-core"))
|
||||||
kapt("com.google.dagger:dagger-compiler:$daggerVersion")
|
kapt("com.google.dagger:dagger-compiler:$daggerVersion")
|
||||||
kaptAndroidTest("com.google.dagger:dagger-compiler:$daggerVersion")
|
kaptAndroidTest("com.google.dagger:dagger-compiler:$daggerVersion")
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -23,10 +23,10 @@ import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
|||||||
import org.isoron.uhabits.core.models.Frequency
|
import org.isoron.uhabits.core.models.Frequency
|
||||||
import org.isoron.uhabits.core.models.Frequency.Companion.DAILY
|
import org.isoron.uhabits.core.models.Frequency.Companion.DAILY
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.Habit.Companion.AT_LEAST
|
|
||||||
import org.isoron.uhabits.core.models.Habit.Companion.NUMBER_HABIT
|
|
||||||
import org.isoron.uhabits.core.models.HabitList
|
import org.isoron.uhabits.core.models.HabitList
|
||||||
|
import org.isoron.uhabits.core.models.HabitType
|
||||||
import org.isoron.uhabits.core.models.ModelFactory
|
import org.isoron.uhabits.core.models.ModelFactory
|
||||||
|
import org.isoron.uhabits.core.models.NumericalHabitType
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.models.Timestamp
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
|
||||||
@@ -102,8 +102,8 @@ class HabitFixtures(private val modelFactory: ModelFactory, private val habitLis
|
|||||||
val habit = modelFactory.buildHabit().apply {
|
val habit = modelFactory.buildHabit().apply {
|
||||||
name = "Read"
|
name = "Read"
|
||||||
question = "How many pages did you walk today?"
|
question = "How many pages did you walk today?"
|
||||||
type = NUMBER_HABIT
|
type = HabitType.NUMERICAL
|
||||||
targetType = AT_LEAST
|
targetType = NumericalHabitType.AT_LEAST
|
||||||
targetValue = 200.0
|
targetValue = 200.0
|
||||||
unit = "pages"
|
unit = "pages"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import org.junit.runner.RunWith
|
|||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
class HabitsTest : BaseUserInterfaceTest() {
|
class HabitsTest : BaseUserInterfaceTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun shouldCreateHabit() {
|
fun shouldCreateHabit() {
|
||||||
@@ -180,6 +181,8 @@ class HabitsTest : BaseUserInterfaceTest() {
|
|||||||
longPressCheckmarks("Wake up early", count = 2)
|
longPressCheckmarks("Wake up early", count = 2)
|
||||||
clickText("Wake up early")
|
clickText("Wake up early")
|
||||||
verifyShowsScreen(SHOW_HABIT)
|
verifyShowsScreen(SHOW_HABIT)
|
||||||
|
// TODO: find a better way than sleeping in tests
|
||||||
|
Thread.sleep(2001L)
|
||||||
verifyDisplaysText("10%")
|
verifyDisplaysText("10%")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +197,8 @@ class HabitsTest : BaseUserInterfaceTest() {
|
|||||||
verifyDoesNotDisplayText("Track time")
|
verifyDoesNotDisplayText("Track time")
|
||||||
verifyDisplaysText("Wake up early")
|
verifyDisplaysText("Wake up early")
|
||||||
longPressCheckmarks("Wake up early", count = 1)
|
longPressCheckmarks("Wake up early", count = 1)
|
||||||
|
// TODO: find a better way than sleeping in tests
|
||||||
|
Thread.sleep(2001L)
|
||||||
verifyDoesNotDisplayText("Wake up early")
|
verifyDoesNotDisplayText("Wake up early")
|
||||||
clickMenu(TOGGLE_COMPLETED)
|
clickMenu(TOGGLE_COMPLETED)
|
||||||
verifyDisplaysText("Track time")
|
verifyDisplaysText("Track time")
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.isoron.uhabits.acceptance.steps
|
package org.isoron.uhabits.acceptance.steps
|
||||||
|
|
||||||
import android.os.Build.VERSION
|
import android.os.Build
|
||||||
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.test.espresso.Espresso
|
import androidx.test.espresso.Espresso
|
||||||
@@ -133,7 +134,7 @@ object CommonSteps : BaseUserInterfaceTest() {
|
|||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun verifyOpensWebsite(url: String?) {
|
fun verifyOpensWebsite(url: String?) {
|
||||||
var browserPkg = "org.chromium.webview_shell"
|
var browserPkg = "org.chromium.webview_shell"
|
||||||
if (VERSION.SDK_INT <= 23) {
|
if (SDK_INT <= Build.VERSION_CODES.M) {
|
||||||
browserPkg = "com.android.browser"
|
browserPkg = "com.android.browser"
|
||||||
}
|
}
|
||||||
assertTrue(device.wait(Until.hasObject(By.pkg(browserPkg)), 5000))
|
assertTrue(device.wait(Until.hasObject(By.pkg(browserPkg)), 5000))
|
||||||
@@ -178,6 +179,22 @@ object CommonSteps : BaseUserInterfaceTest() {
|
|||||||
EditHabitSteps.clickSave()
|
EditHabitSteps.clickSave()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun changeFrequencyToDaily(habitName: String) {
|
||||||
|
clickText(habitName)
|
||||||
|
Espresso.onView(ViewMatchers.withId(R.id.action_edit_habit)).perform(ViewActions.click())
|
||||||
|
EditHabitSteps.pickDailyFrequency()
|
||||||
|
EditHabitSteps.clickSave()
|
||||||
|
pressBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeFrequencyToMonthly(habitName: String) {
|
||||||
|
clickText(habitName)
|
||||||
|
Espresso.onView(ViewMatchers.withId(R.id.action_edit_habit)).perform(ViewActions.click())
|
||||||
|
EditHabitSteps.pickMonthFrequency()
|
||||||
|
EditHabitSteps.clickSave()
|
||||||
|
pressBack()
|
||||||
|
}
|
||||||
|
|
||||||
enum class Screen {
|
enum class Screen {
|
||||||
LIST_HABITS, SHOW_HABIT, EDIT_HABIT, SELECT_HABIT_TYPE
|
LIST_HABITS, SHOW_HABIT, EDIT_HABIT, SELECT_HABIT_TYPE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,24 @@ object EditHabitSteps {
|
|||||||
Espresso.onView(ViewMatchers.withText("SAVE")).perform(ViewActions.click())
|
Espresso.onView(ViewMatchers.withText("SAVE")).perform(ViewActions.click())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun pickMonthFrequency() {
|
||||||
|
Espresso.onView(ViewMatchers.withId(R.id.boolean_frequency_picker))
|
||||||
|
.perform(ViewActions.click())
|
||||||
|
Espresso.onView(ViewMatchers.withId(R.id.xTimesPerMonthRadioButton))
|
||||||
|
.perform(ViewActions.click())
|
||||||
|
Espresso.onView(ViewMatchers.withId(R.id.xTimesPerMonthTextView))
|
||||||
|
.perform(ViewActions.replaceText("1"))
|
||||||
|
Espresso.onView(ViewMatchers.withText("SAVE")).perform(ViewActions.click())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pickDailyFrequency() {
|
||||||
|
Espresso.onView(ViewMatchers.withId(R.id.boolean_frequency_picker))
|
||||||
|
.perform(ViewActions.click())
|
||||||
|
Espresso.onView(ViewMatchers.withId(R.id.everyDayRadioButton))
|
||||||
|
.perform(ViewActions.click())
|
||||||
|
Espresso.onView(ViewMatchers.withText("SAVE")).perform(ViewActions.click())
|
||||||
|
}
|
||||||
|
|
||||||
fun pickColor(color: Int) {
|
fun pickColor(color: Int) {
|
||||||
Espresso.onView(ViewMatchers.withId(R.id.colorButton)).perform(ViewActions.click())
|
Espresso.onView(ViewMatchers.withId(R.id.colorButton)).perform(ViewActions.click())
|
||||||
BaseUserInterfaceTest.device.findObject(By.descStartsWith(String.format("Color %d", color)))
|
BaseUserInterfaceTest.device.findObject(By.descStartsWith(String.format("Color %d", color)))
|
||||||
|
|||||||
@@ -120,6 +120,12 @@ object ListHabitsSteps {
|
|||||||
BaseUserInterfaceTest.device.waitForIdle()
|
BaseUserInterfaceTest.device.waitForIdle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun changeSort(sortText: String) {
|
||||||
|
clickViewWithId(R.id.action_filter)
|
||||||
|
Espresso.onView(ViewMatchers.withText("Sort")).perform(ViewActions.click())
|
||||||
|
Espresso.onView(ViewMatchers.withText(sortText)).perform(ViewActions.click())
|
||||||
|
}
|
||||||
|
|
||||||
enum class MenuItem {
|
enum class MenuItem {
|
||||||
ABOUT, HELP, SETTINGS, EDIT, DELETE, ARCHIVE, TOGGLE_ARCHIVED, UNARCHIVE, TOGGLE_COMPLETED, ADD
|
ABOUT, HELP, SETTINGS, EDIT, DELETE, ARCHIVE, TOGGLE_ARCHIVED, UNARCHIVE, TOGGLE_COMPLETED, ADD
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.isoron.uhabits.acceptance.steps
|
package org.isoron.uhabits.acceptance.steps
|
||||||
|
|
||||||
import android.os.Build.VERSION
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import androidx.test.uiautomator.UiScrollable
|
import androidx.test.uiautomator.UiScrollable
|
||||||
import androidx.test.uiautomator.UiSelector
|
import androidx.test.uiautomator.UiSelector
|
||||||
import junit.framework.Assert.assertFalse
|
import junit.framework.Assert.assertFalse
|
||||||
@@ -50,29 +50,21 @@ object WidgetSteps {
|
|||||||
private fun openWidgetScreen() {
|
private fun openWidgetScreen() {
|
||||||
val h = BaseUserInterfaceTest.device.displayHeight
|
val h = BaseUserInterfaceTest.device.displayHeight
|
||||||
val w = BaseUserInterfaceTest.device.displayWidth
|
val w = BaseUserInterfaceTest.device.displayWidth
|
||||||
if (VERSION.SDK_INT <= 21) {
|
val listId = "com.android.launcher3:id/widgets_list_view"
|
||||||
BaseUserInterfaceTest.device.pressHome()
|
BaseUserInterfaceTest.device.pressHome()
|
||||||
BaseUserInterfaceTest.device.waitForIdle()
|
BaseUserInterfaceTest.device.waitForIdle()
|
||||||
BaseUserInterfaceTest.device.findObject(UiSelector().description("Apps")).click()
|
BaseUserInterfaceTest.device.drag(w / 2, h / 2, w / 2, h / 2, 8)
|
||||||
BaseUserInterfaceTest.device.findObject(UiSelector().description("Apps")).click()
|
var button = BaseUserInterfaceTest.device.findObject(UiSelector().text("WIDGETS"))
|
||||||
BaseUserInterfaceTest.device.findObject(UiSelector().description("Widgets")).click()
|
if (!button.waitForExists(1000)) {
|
||||||
} else {
|
button = BaseUserInterfaceTest.device.findObject(UiSelector().text("Widgets"))
|
||||||
val listId = "com.android.launcher3:id/widgets_list_view"
|
|
||||||
BaseUserInterfaceTest.device.pressHome()
|
|
||||||
BaseUserInterfaceTest.device.waitForIdle()
|
|
||||||
BaseUserInterfaceTest.device.drag(w / 2, h / 2, w / 2, h / 2, 8)
|
|
||||||
var button = BaseUserInterfaceTest.device.findObject(UiSelector().text("WIDGETS"))
|
|
||||||
if (!button.waitForExists(1000)) {
|
|
||||||
button = BaseUserInterfaceTest.device.findObject(UiSelector().text("Widgets"))
|
|
||||||
}
|
|
||||||
button.click()
|
|
||||||
if (VERSION.SDK_INT >= 28) {
|
|
||||||
UiScrollable(UiSelector().resourceId(listId))
|
|
||||||
.scrollForward()
|
|
||||||
}
|
|
||||||
UiScrollable(UiSelector().resourceId(listId))
|
|
||||||
.scrollIntoView(UiSelector().text("Checkmark"))
|
|
||||||
}
|
}
|
||||||
|
button.click()
|
||||||
|
if (SDK_INT >= 28) {
|
||||||
|
UiScrollable(UiSelector().resourceId(listId))
|
||||||
|
.scrollForward()
|
||||||
|
}
|
||||||
|
UiScrollable(UiSelector().resourceId(listId))
|
||||||
|
.scrollIntoView(UiSelector().text("Checkmark"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import androidx.test.filters.MediumTest
|
|||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter.Companion.buildState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter.Companion.buildState
|
||||||
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
import org.isoron.uhabits.utils.toFixedAndroidColor
|
import org.isoron.uhabits.utils.toFixedAndroidColor
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -39,7 +40,12 @@ class ScoreChartTest : BaseViewTest() {
|
|||||||
super.setUp()
|
super.setUp()
|
||||||
fixtures.purgeHabits(habitList)
|
fixtures.purgeHabits(habitList)
|
||||||
habit = fixtures.createLongHabit()
|
habit = fixtures.createLongHabit()
|
||||||
val state = buildState(habit, prefs.firstWeekdayInt, 0)
|
val state = buildState(
|
||||||
|
habit = habit,
|
||||||
|
firstWeekday = prefs.firstWeekdayInt,
|
||||||
|
spinnerPosition = 0,
|
||||||
|
theme = LightTheme(),
|
||||||
|
)
|
||||||
view = ScoreChart(targetContext).apply {
|
view = ScoreChart(targetContext).apply {
|
||||||
setScores(state.scores)
|
setScores(state.scores)
|
||||||
setColor(state.color.toFixedAndroidColor())
|
setColor(state.color.toFixedAndroidColor())
|
||||||
@@ -72,7 +78,7 @@ class ScoreChartTest : BaseViewTest() {
|
|||||||
@Test
|
@Test
|
||||||
@Throws(Throwable::class)
|
@Throws(Throwable::class)
|
||||||
fun testRender_withMonthlyBucket() {
|
fun testRender_withMonthlyBucket() {
|
||||||
val (scores, bucketSize) = buildState(habit, prefs.firstWeekdayInt, 2)
|
val (scores, bucketSize) = buildState(habit, prefs.firstWeekdayInt, 2, LightTheme())
|
||||||
view.setScores(scores)
|
view.setScores(scores)
|
||||||
view.setBucketSize(bucketSize)
|
view.setBucketSize(bucketSize)
|
||||||
view.invalidate()
|
view.invalidate()
|
||||||
@@ -89,7 +95,7 @@ class ScoreChartTest : BaseViewTest() {
|
|||||||
@Test
|
@Test
|
||||||
@Throws(Throwable::class)
|
@Throws(Throwable::class)
|
||||||
fun testRender_withYearlyBucket() {
|
fun testRender_withYearlyBucket() {
|
||||||
val state = buildState(habit, prefs.firstWeekdayInt, 4)
|
val state = buildState(habit, prefs.firstWeekdayInt, 4, LightTheme())
|
||||||
view.setScores(state.scores)
|
view.setScores(state.scores)
|
||||||
view.setBucketSize(state.bucketSize)
|
view.setBucketSize(state.bucketSize)
|
||||||
view.invalidate()
|
view.invalidate()
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class EntryButtonViewTest : BaseViewTest() {
|
|||||||
lateinit var view: CheckmarkButtonView
|
lateinit var view: CheckmarkButtonView
|
||||||
|
|
||||||
var toggled = false
|
var toggled = false
|
||||||
|
var edited = false
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
override fun setUp() {
|
override fun setUp() {
|
||||||
@@ -43,7 +44,8 @@ class EntryButtonViewTest : BaseViewTest() {
|
|||||||
view = component.getEntryButtonViewFactory().create().apply {
|
view = component.getEntryButtonViewFactory().create().apply {
|
||||||
value = Entry.NO
|
value = Entry.NO
|
||||||
color = PaletteUtils.getAndroidTestColor(5)
|
color = PaletteUtils.getAndroidTestColor(5)
|
||||||
onToggle = { toggled = true }
|
onToggle = { _, _, _ -> toggled = true }
|
||||||
|
onEdit = { edited = true }
|
||||||
}
|
}
|
||||||
measureView(view, dpToPixels(48), dpToPixels(48))
|
measureView(view, dpToPixels(48), dpToPixels(48))
|
||||||
}
|
}
|
||||||
@@ -70,20 +72,28 @@ class EntryButtonViewTest : BaseViewTest() {
|
|||||||
fun testClick_withShortToggleDisabled() {
|
fun testClick_withShortToggleDisabled() {
|
||||||
prefs.isShortToggleEnabled = false
|
prefs.isShortToggleEnabled = false
|
||||||
view.performClick()
|
view.performClick()
|
||||||
assertFalse(toggled)
|
assertTrue(!toggled and edited)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testClick_withShortToggleEnabled() {
|
fun testClick_withShortToggleEnabled() {
|
||||||
prefs.isShortToggleEnabled = true
|
prefs.isShortToggleEnabled = true
|
||||||
view.performClick()
|
view.performClick()
|
||||||
assertTrue(toggled)
|
assertTrue(toggled and !edited)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLongClick() {
|
fun testLongClick_withShortToggleDisabled() {
|
||||||
|
prefs.isShortToggleEnabled = false
|
||||||
view.performLongClick()
|
view.performLongClick()
|
||||||
assertTrue(toggled)
|
assertTrue(toggled and !edited)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLongClick_withShortToggleEnabled() {
|
||||||
|
prefs.isShortToggleEnabled = true
|
||||||
|
view.performLongClick()
|
||||||
|
assertTrue(!toggled and edited)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assertRendersCheckedExplicitly() {
|
private fun assertRendersCheckedExplicitly() {
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class EntryPanelViewTest : BaseViewTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun testToggle() {
|
fun testToggle() {
|
||||||
val timestamps = mutableListOf<Timestamp>()
|
val timestamps = mutableListOf<Timestamp>()
|
||||||
view.onToggle = { t, _ -> timestamps.add(t) }
|
view.onToggle = { t, _, _, _ -> timestamps.add(t) }
|
||||||
view.buttons[0].performLongClick()
|
view.buttons[0].performLongClick()
|
||||||
view.buttons[2].performLongClick()
|
view.buttons[2].performLongClick()
|
||||||
view.buttons[3].performLongClick()
|
view.buttons[3].performLongClick()
|
||||||
@@ -88,7 +88,7 @@ class EntryPanelViewTest : BaseViewTest() {
|
|||||||
fun testToggle_withOffset() {
|
fun testToggle_withOffset() {
|
||||||
val timestamps = mutableListOf<Timestamp>()
|
val timestamps = mutableListOf<Timestamp>()
|
||||||
view.dataOffset = 3
|
view.dataOffset = 3
|
||||||
view.onToggle = { t, _ -> timestamps += t }
|
view.onToggle = { t, _, _, _ -> timestamps += t }
|
||||||
view.buttons[0].performLongClick()
|
view.buttons[0].performLongClick()
|
||||||
view.buttons[2].performLongClick()
|
view.buttons[2].performLongClick()
|
||||||
view.buttons[3].performLongClick()
|
view.buttons[3].performLongClick()
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import androidx.test.filters.MediumTest
|
|||||||
import org.hamcrest.CoreMatchers.equalTo
|
import org.hamcrest.CoreMatchers.equalTo
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
|
import org.isoron.uhabits.core.models.NumericalHabitType
|
||||||
import org.isoron.uhabits.utils.PaletteUtils
|
import org.isoron.uhabits.utils.PaletteUtils
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -42,6 +43,7 @@ class NumberButtonViewTest : BaseViewTest() {
|
|||||||
super.setUp()
|
super.setUp()
|
||||||
view = component.getNumberButtonViewFactory().create().apply {
|
view = component.getNumberButtonViewFactory().create().apply {
|
||||||
units = "steps"
|
units = "steps"
|
||||||
|
targetType = NumericalHabitType.AT_LEAST
|
||||||
threshold = 100.0
|
threshold = 100.0
|
||||||
color = PaletteUtils.getAndroidTestColor(8)
|
color = PaletteUtils.getAndroidTestColor(8)
|
||||||
onEdit = { edited = true }
|
onEdit = { edited = true }
|
||||||
@@ -74,10 +76,10 @@ class NumberButtonViewTest : BaseViewTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRender_emptyUnits() {
|
fun testRender_atMostAboveThreshold() {
|
||||||
view.value = 500.0
|
view.value = 500.0
|
||||||
view.units = ""
|
view.targetType = NumericalHabitType.AT_MOST
|
||||||
assertRenders(view, "$PATH/render_unitless.png")
|
assertRenders(view, "$PATH/render_at_most_above.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -86,6 +88,13 @@ class NumberButtonViewTest : BaseViewTest() {
|
|||||||
assertRenders(view, "$PATH/render_below.png")
|
assertRenders(view, "$PATH/render_below.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRender_atMostBetweenThresholds() {
|
||||||
|
view.value = 110.0
|
||||||
|
view.targetType = NumericalHabitType.AT_MOST
|
||||||
|
assertRenders(view, "$PATH/render_at_most_between.png")
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRender_zero() {
|
fun testRender_zero() {
|
||||||
view.value = 0.0
|
view.value = 0.0
|
||||||
@@ -93,15 +102,21 @@ class NumberButtonViewTest : BaseViewTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testClick_shortToggleDisabled() {
|
fun testRender_atMostBelowThreshold() {
|
||||||
prefs.isShortToggleEnabled = false
|
view.value = 0.0
|
||||||
view.performClick()
|
view.targetType = NumericalHabitType.AT_MOST
|
||||||
assertFalse(edited)
|
assertRenders(view, "$PATH/render_at_most_below.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testClick_shortToggleEnabled() {
|
fun testRender_emptyUnits() {
|
||||||
prefs.isShortToggleEnabled = true
|
view.value = 500.0
|
||||||
|
view.units = ""
|
||||||
|
assertRenders(view, "$PATH/render_unitless.png")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testClick() {
|
||||||
view.performClick()
|
view.performClick()
|
||||||
assertTrue(edited)
|
assertTrue(edited)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import androidx.test.filters.MediumTest
|
|||||||
import org.hamcrest.CoreMatchers.equalTo
|
import org.hamcrest.CoreMatchers.equalTo
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
|
import org.isoron.uhabits.core.models.NumericalHabitType
|
||||||
import org.isoron.uhabits.core.models.Timestamp
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
import org.isoron.uhabits.utils.PaletteUtils
|
import org.isoron.uhabits.utils.PaletteUtils
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
@@ -55,6 +56,7 @@ class NumberPanelViewTest : BaseViewTest() {
|
|||||||
buttonCount = 4
|
buttonCount = 4
|
||||||
color = PaletteUtils.getAndroidTestColor(7)
|
color = PaletteUtils.getAndroidTestColor(7)
|
||||||
units = "steps"
|
units = "steps"
|
||||||
|
targetType = NumericalHabitType.AT_LEAST
|
||||||
threshold = 5000.0
|
threshold = 5000.0
|
||||||
}
|
}
|
||||||
view.onAttachedToWindow()
|
view.onAttachedToWindow()
|
||||||
@@ -74,7 +76,7 @@ class NumberPanelViewTest : BaseViewTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun testEdit() {
|
fun testEdit() {
|
||||||
val timestamps = mutableListOf<Timestamp>()
|
val timestamps = mutableListOf<Timestamp>()
|
||||||
view.onEdit = { timestamps.plusAssign(it) }
|
view.onEdit = { t -> timestamps.plusAssign(t) }
|
||||||
view.buttons[0].performLongClick()
|
view.buttons[0].performLongClick()
|
||||||
view.buttons[2].performLongClick()
|
view.buttons[2].performLongClick()
|
||||||
view.buttons[3].performLongClick()
|
view.buttons[3].performLongClick()
|
||||||
@@ -85,7 +87,7 @@ class NumberPanelViewTest : BaseViewTest() {
|
|||||||
fun testEdit_withOffset() {
|
fun testEdit_withOffset() {
|
||||||
val timestamps = mutableListOf<Timestamp>()
|
val timestamps = mutableListOf<Timestamp>()
|
||||||
view.dataOffset = 3
|
view.dataOffset = 3
|
||||||
view.onEdit = { timestamps += it }
|
view.onEdit = { t -> timestamps += t }
|
||||||
view.buttons[0].performLongClick()
|
view.buttons[0].performLongClick()
|
||||||
view.buttons[2].performLongClick()
|
view.buttons[2].performLongClick()
|
||||||
view.buttons[3].performLongClick()
|
view.buttons[3].performLongClick()
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import androidx.test.filters.MediumTest
|
|||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardPresenter
|
||||||
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -43,7 +44,13 @@ class FrequencyCardViewTest : BaseViewTest() {
|
|||||||
.from(targetContext)
|
.from(targetContext)
|
||||||
.inflate(R.layout.show_habit, null)
|
.inflate(R.layout.show_habit, null)
|
||||||
.findViewById<View>(R.id.frequencyCard) as FrequencyCardView
|
.findViewById<View>(R.id.frequencyCard) as FrequencyCardView
|
||||||
view.setState(FrequencyCardPresenter.buildState(habit = habit, firstWeekday = 0))
|
view.setState(
|
||||||
|
FrequencyCardPresenter.buildState(
|
||||||
|
habit = habit,
|
||||||
|
firstWeekday = 0,
|
||||||
|
theme = LightTheme(),
|
||||||
|
)
|
||||||
|
)
|
||||||
measureView(view, 800f, 600f)
|
measureView(view, 800f, 600f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.isoron.uhabits.BaseViewTest
|
|||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardState
|
||||||
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -50,6 +51,7 @@ class OverviewCardViewTest : BaseViewTest() {
|
|||||||
scoreYearDiff = 0.74f,
|
scoreYearDiff = 0.74f,
|
||||||
totalCount = 44,
|
totalCount = 44,
|
||||||
color = PaletteColor(7),
|
color = PaletteColor(7),
|
||||||
|
theme = LightTheme(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
measureView(view, 800f, 300f)
|
measureView(view, 800f, 300f)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import androidx.test.filters.MediumTest
|
|||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter.Companion.buildState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter.Companion.buildState
|
||||||
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -43,7 +44,14 @@ class ScoreCardViewTest : BaseViewTest() {
|
|||||||
.from(targetContext)
|
.from(targetContext)
|
||||||
.inflate(R.layout.show_habit, null)
|
.inflate(R.layout.show_habit, null)
|
||||||
.findViewById<View>(R.id.scoreCard) as ScoreCardView
|
.findViewById<View>(R.id.scoreCard) as ScoreCardView
|
||||||
view.setState(buildState(habit = habit, firstWeekday = 0, spinnerPosition = 0))
|
view.setState(
|
||||||
|
buildState(
|
||||||
|
habit = habit,
|
||||||
|
firstWeekday = 0,
|
||||||
|
spinnerPosition = 0,
|
||||||
|
theme = LightTheme(),
|
||||||
|
)
|
||||||
|
)
|
||||||
measureView(view, 800f, 600f)
|
measureView(view, 800f, 600f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import androidx.test.filters.MediumTest
|
|||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardState
|
||||||
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -47,6 +48,7 @@ class StreakCardViewTest : BaseViewTest() {
|
|||||||
StreakCardState(
|
StreakCardState(
|
||||||
bestStreaks = habit.streaks.getBest(10),
|
bestStreaks = habit.streaks.getBest(10),
|
||||||
color = habit.color,
|
color = habit.color,
|
||||||
|
theme = LightTheme(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
measureView(view, 800f, 600f)
|
measureView(view, 800f, 600f)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.isoron.uhabits.core.models.PaletteColor
|
|||||||
import org.isoron.uhabits.core.models.Reminder
|
import org.isoron.uhabits.core.models.Reminder
|
||||||
import org.isoron.uhabits.core.models.WeekdayList.Companion.EVERY_DAY
|
import org.isoron.uhabits.core.models.WeekdayList.Companion.EVERY_DAY
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
|
||||||
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -52,8 +53,7 @@ class SubtitleCardViewTest : BaseViewTest() {
|
|||||||
isNumerical = false,
|
isNumerical = false,
|
||||||
question = "Did you meditate this morning?",
|
question = "Did you meditate this morning?",
|
||||||
reminder = Reminder(8, 30, EVERY_DAY),
|
reminder = Reminder(8, 30, EVERY_DAY),
|
||||||
unit = "",
|
theme = LightTheme(),
|
||||||
targetValue = 0.0,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
measureView(view, 800f, 200f)
|
measureView(view, 800f, 200f)
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class PerformanceTest : BaseAndroidTest() {
|
|||||||
val habit = fixtures.createEmptyHabit()
|
val habit = fixtures.createEmptyHabit()
|
||||||
for (i in 0..4999) {
|
for (i in 0..4999) {
|
||||||
val timestamp: Timestamp = Timestamp(i * DAY_LENGTH)
|
val timestamp: Timestamp = Timestamp(i * DAY_LENGTH)
|
||||||
CreateRepetitionCommand(habitList, habit, timestamp, 1).run()
|
CreateRepetitionCommand(habitList, habit, timestamp, 1, "").run()
|
||||||
}
|
}
|
||||||
db.setTransactionSuccessful()
|
db.setTransactionSuccessful()
|
||||||
db.endTransaction()
|
db.endTransaction()
|
||||||
|
|||||||
@@ -21,9 +21,12 @@ package org.isoron.uhabits.regression
|
|||||||
|
|
||||||
import androidx.test.filters.LargeTest
|
import androidx.test.filters.LargeTest
|
||||||
import org.isoron.uhabits.BaseUserInterfaceTest
|
import org.isoron.uhabits.BaseUserInterfaceTest
|
||||||
|
import org.isoron.uhabits.acceptance.steps.CommonSteps
|
||||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.EDIT_HABIT
|
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.EDIT_HABIT
|
||||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.LIST_HABITS
|
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.LIST_HABITS
|
||||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.SELECT_HABIT_TYPE
|
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.SELECT_HABIT_TYPE
|
||||||
|
import org.isoron.uhabits.acceptance.steps.CommonSteps.changeFrequencyToDaily
|
||||||
|
import org.isoron.uhabits.acceptance.steps.CommonSteps.changeFrequencyToMonthly
|
||||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.clickText
|
import org.isoron.uhabits.acceptance.steps.CommonSteps.clickText
|
||||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.createHabit
|
import org.isoron.uhabits.acceptance.steps.CommonSteps.createHabit
|
||||||
import org.isoron.uhabits.acceptance.steps.CommonSteps.launchApp
|
import org.isoron.uhabits.acceptance.steps.CommonSteps.launchApp
|
||||||
@@ -37,9 +40,12 @@ import org.isoron.uhabits.acceptance.steps.EditHabitSteps.clickSave
|
|||||||
import org.isoron.uhabits.acceptance.steps.EditHabitSteps.typeName
|
import org.isoron.uhabits.acceptance.steps.EditHabitSteps.typeName
|
||||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.ADD
|
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.ADD
|
||||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.DELETE
|
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.DELETE
|
||||||
|
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.changeSort
|
||||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.clickMenu
|
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.clickMenu
|
||||||
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.longPressCheckmarks
|
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.longPressCheckmarks
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.NO
|
||||||
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
|
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO
|
||||||
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
@@ -83,4 +89,37 @@ class ListHabitsRegressionTest : BaseUserInterfaceTest() {
|
|||||||
offsetHeaders()
|
offsetHeaders()
|
||||||
verifyDisplaysCheckmarks("Wake up early", listOf(UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN))
|
verifyDisplaysCheckmarks("Wake up early", listOf(UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://github.com/iSoron/uhabits/issues/1131
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun should_refresh_sort_after_habit_edit() {
|
||||||
|
launchApp()
|
||||||
|
verifyShowsScreen(LIST_HABITS)
|
||||||
|
changeSort("By score")
|
||||||
|
changeSort("By status")
|
||||||
|
longPressCheckmarks("Meditate", count = 1)
|
||||||
|
changeFrequencyToMonthly("Read books")
|
||||||
|
longPressCheckmarks("Read books", count = 2)
|
||||||
|
longPressCheckmarks("Read books", count = 1)
|
||||||
|
verifyDisplaysCheckmarks("Meditate", listOf(YES_AUTO, YES_MANUAL, YES_AUTO, YES_MANUAL))
|
||||||
|
CommonSteps.verifyDisplaysTextInSequence(
|
||||||
|
"Wake up early",
|
||||||
|
"Read books",
|
||||||
|
"Meditate",
|
||||||
|
"Track time"
|
||||||
|
)
|
||||||
|
|
||||||
|
changeFrequencyToDaily("Meditate")
|
||||||
|
|
||||||
|
verifyDisplaysCheckmarks("Meditate", listOf(NO, YES_MANUAL, UNKNOWN, YES_MANUAL))
|
||||||
|
CommonSteps.verifyDisplaysTextInSequence(
|
||||||
|
"Wake up early",
|
||||||
|
"Meditate",
|
||||||
|
"Read books",
|
||||||
|
"Track time",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import android.widget.FrameLayout
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.filters.MediumTest
|
import androidx.test.filters.MediumTest
|
||||||
import org.hamcrest.CoreMatchers
|
import org.hamcrest.CoreMatchers
|
||||||
import org.hamcrest.CoreMatchers.`is`
|
|
||||||
import org.hamcrest.CoreMatchers.equalTo
|
import org.hamcrest.CoreMatchers.equalTo
|
||||||
|
import org.hamcrest.CoreMatchers.`is`
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
|||||||
@@ -17,9 +17,10 @@
|
|||||||
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
package="org.isoron.uhabits">
|
package="org.isoron.uhabits">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
|
||||||
|
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
|
||||||
@@ -48,11 +49,11 @@
|
|||||||
android:name=".activities.habits.list.ListHabitsActivity"
|
android:name=".activities.habits.list.ListHabitsActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/main_activity_title"
|
android:label="@string/main_activity_title"
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop" />
|
||||||
</activity>
|
|
||||||
|
|
||||||
<activity-alias
|
<activity-alias
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/main_activity_title"
|
android:label="@string/main_activity_title"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:targetActivity=".activities.habits.list.ListHabitsActivity">
|
android:targetActivity=".activities.habits.list.ListHabitsActivity">
|
||||||
@@ -85,6 +86,7 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".widgets.activities.HabitPickerDialog"
|
android:name=".widgets.activities.HabitPickerDialog"
|
||||||
|
android:exported="true"
|
||||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
@@ -93,6 +95,7 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".widgets.activities.BooleanHabitPickerDialog"
|
android:name=".widgets.activities.BooleanHabitPickerDialog"
|
||||||
|
android:exported="true"
|
||||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
@@ -101,6 +104,7 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".widgets.activities.NumericalHabitPickerDialog"
|
android:name=".widgets.activities.NumericalHabitPickerDialog"
|
||||||
|
android:exported="true"
|
||||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
@@ -117,10 +121,11 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".widgets.activities.NumericalCheckmarkWidgetActivity"
|
android:name=".widgets.activities.NumericalCheckmarkWidgetActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:exported="true"
|
||||||
android:label="NumericalCheckmarkWidget"
|
android:label="NumericalCheckmarkWidget"
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
android:excludeFromRecents="true"
|
android:theme="@style/Theme.Transparent">
|
||||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.isoron.uhabits.ACTION_SHOW_NUMERICAL_VALUE_ACTIVITY" />
|
<action android:name="org.isoron.uhabits.ACTION_SHOW_NUMERICAL_VALUE_ACTIVITY" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -128,13 +133,14 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".notifications.SnoozeDelayPickerActivity"
|
android:name=".notifications.SnoozeDelayPickerActivity"
|
||||||
android:taskAffinity=""
|
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleInstance"
|
||||||
|
android:taskAffinity=""
|
||||||
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.CheckmarkWidgetProvider"
|
android:name=".widgets.CheckmarkWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/checkmark">
|
android:label="@string/checkmark">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
@@ -152,6 +158,7 @@
|
|||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.HistoryWidgetProvider"
|
android:name=".widgets.HistoryWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/history">
|
android:label="@string/history">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
@@ -164,6 +171,7 @@
|
|||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.ScoreWidgetProvider"
|
android:name=".widgets.ScoreWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/score">
|
android:label="@string/score">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
@@ -176,6 +184,7 @@
|
|||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.StreakWidgetProvider"
|
android:name=".widgets.StreakWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/streaks">
|
android:label="@string/streaks">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
@@ -188,6 +197,7 @@
|
|||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.FrequencyWidgetProvider"
|
android:name=".widgets.FrequencyWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/frequency">
|
android:label="@string/frequency">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
@@ -200,6 +210,7 @@
|
|||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.TargetWidgetProvider"
|
android:name=".widgets.TargetWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/target">
|
android:label="@string/target">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
@@ -210,13 +221,17 @@
|
|||||||
android:resource="@xml/widget_target_info" />
|
android:resource="@xml/widget_target_info" />
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receivers.ReminderReceiver">
|
<receiver
|
||||||
|
android:name=".receivers.ReminderReceiver"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receivers.WidgetReceiver">
|
<receiver android:name=".receivers.WidgetReceiver"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="false">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<action android:name="org.isoron.uhabits.ACTION_SET_NUMERICAL_VALUE" />
|
<action android:name="org.isoron.uhabits.ACTION_SET_NUMERICAL_VALUE" />
|
||||||
@@ -267,7 +282,7 @@
|
|||||||
<!-- Locale/Tasker -->
|
<!-- Locale/Tasker -->
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".automation.FireSettingReceiver"
|
android:name=".automation.FireSettingReceiver"
|
||||||
android:exported="true">
|
android:exported="false">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
|
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class AndroidCanvas : Canvas {
|
|||||||
var innerDensity = 1.0
|
var innerDensity = 1.0
|
||||||
var innerWidth = 0
|
var innerWidth = 0
|
||||||
var innerHeight = 0
|
var innerHeight = 0
|
||||||
|
var mHeight = 15
|
||||||
|
|
||||||
var paint = Paint().apply {
|
var paint = Paint().apply {
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
@@ -64,11 +65,10 @@ class AndroidCanvas : Canvas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun drawText(text: String, x: Double, y: Double) {
|
override fun drawText(text: String, x: Double, y: Double) {
|
||||||
textPaint.getTextBounds(text, 0, text.length, textBounds)
|
|
||||||
innerCanvas.drawText(
|
innerCanvas.drawText(
|
||||||
text,
|
text,
|
||||||
x.toDp(),
|
x.toDp(),
|
||||||
y.toDp() - textBounds.exactCenterY(),
|
y.toDp() + 0.6f * mHeight,
|
||||||
textPaint,
|
textPaint,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -126,10 +126,17 @@ class AndroidCanvas : Canvas {
|
|||||||
Font.BOLD -> Typeface.DEFAULT_BOLD
|
Font.BOLD -> Typeface.DEFAULT_BOLD
|
||||||
Font.FONT_AWESOME -> getFontAwesome(context)
|
Font.FONT_AWESOME -> getFontAwesome(context)
|
||||||
}
|
}
|
||||||
|
updateMHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setFontSize(size: Double) {
|
override fun setFontSize(size: Double) {
|
||||||
textPaint.textSize = size.toDp()
|
textPaint.textSize = size.toDp()
|
||||||
|
updateMHeight()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateMHeight() {
|
||||||
|
textPaint.getTextBounds("m", 0, 1, textBounds)
|
||||||
|
mHeight = textBounds.height()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setStrokeWidth(size: Double) {
|
override fun setStrokeWidth(size: Double) {
|
||||||
|
|||||||
@@ -49,23 +49,12 @@ class AndroidDataView(
|
|||||||
override fun onShowPress(e: MotionEvent?) = Unit
|
override fun onShowPress(e: MotionEvent?) = Unit
|
||||||
|
|
||||||
override fun onSingleTapUp(e: MotionEvent?): Boolean {
|
override fun onSingleTapUp(e: MotionEvent?): Boolean {
|
||||||
val x: Float
|
return handleClick(e, true)
|
||||||
val y: Float
|
|
||||||
try {
|
|
||||||
val pointerId = e!!.getPointerId(0)
|
|
||||||
x = e.getX(pointerId)
|
|
||||||
y = e.getY(pointerId)
|
|
||||||
} catch (ex: RuntimeException) {
|
|
||||||
// Android often throws IllegalArgumentException here. Apparently,
|
|
||||||
// the pointer id may become invalid shortly after calling
|
|
||||||
// e.getPointerId.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
view?.onClick(x / canvas.innerDensity, y / canvas.innerDensity)
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongPress(e: MotionEvent?) = Unit
|
override fun onLongPress(e: MotionEvent?) {
|
||||||
|
handleClick(e)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onScroll(
|
override fun onScroll(
|
||||||
e1: MotionEvent?,
|
e1: MotionEvent?,
|
||||||
@@ -137,4 +126,22 @@ class AndroidDataView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleClick(e: MotionEvent?, isSingleTap: Boolean = false): Boolean {
|
||||||
|
val x: Float
|
||||||
|
val y: Float
|
||||||
|
try {
|
||||||
|
val pointerId = e!!.getPointerId(0)
|
||||||
|
x = e.getX(pointerId)
|
||||||
|
y = e.getY(pointerId)
|
||||||
|
} catch (ex: RuntimeException) {
|
||||||
|
// Android often throws IllegalArgumentException here. Apparently,
|
||||||
|
// the pointer id may become invalid shortly after calling
|
||||||
|
// e.getPointerId.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (isSingleTap) view?.onClick(x / canvas.innerDensity, y / canvas.innerDensity)
|
||||||
|
else view?.onLongClick(x / canvas.innerDensity, y / canvas.innerDensity)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import org.isoron.uhabits.core.preferences.Preferences
|
|||||||
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
||||||
import org.isoron.uhabits.core.ui.views.DarkTheme
|
import org.isoron.uhabits.core.ui.views.DarkTheme
|
||||||
import org.isoron.uhabits.core.ui.views.LightTheme
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
|
import org.isoron.uhabits.core.ui.views.PureBlackTheme
|
||||||
import org.isoron.uhabits.core.ui.views.Theme
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
import org.isoron.uhabits.inject.ActivityScope
|
import org.isoron.uhabits.inject.ActivityScope
|
||||||
@@ -66,7 +67,7 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun applyPureBlackTheme() {
|
override fun applyPureBlackTheme() {
|
||||||
currentTheme = DarkTheme()
|
currentTheme = PureBlackTheme()
|
||||||
context.setTheme(R.style.AppBaseThemeDark_PureBlack)
|
context.setTheme(R.style.AppBaseThemeDark_PureBlack)
|
||||||
(context as Activity).window.navigationBarColor =
|
(context as Activity).window.navigationBarColor =
|
||||||
ContextCompat.getColor(context, R.color.black)
|
ContextCompat.getColor(context, R.color.black)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.isoron.uhabits.BuildConfig
|
|||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.databinding.AboutBinding
|
import org.isoron.uhabits.databinding.AboutBinding
|
||||||
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
import org.isoron.uhabits.utils.setupToolbar
|
import org.isoron.uhabits.utils.setupToolbar
|
||||||
|
|
||||||
@SuppressLint("ViewConstructor")
|
@SuppressLint("ViewConstructor")
|
||||||
@@ -41,7 +42,8 @@ class AboutView(
|
|||||||
setupToolbar(
|
setupToolbar(
|
||||||
toolbar = binding.toolbar,
|
toolbar = binding.toolbar,
|
||||||
color = PaletteColor(11),
|
color = PaletteColor(11),
|
||||||
title = resources.getString(R.string.about)
|
title = resources.getString(R.string.about),
|
||||||
|
theme = currentTheme(),
|
||||||
)
|
)
|
||||||
val version = resources.getString(R.string.version_n)
|
val version = resources.getString(R.string.version_n)
|
||||||
binding.tvContributors.setOnClickListener { screen.showCodeContributorsWebsite() }
|
binding.tvContributors.setOnClickListener { screen.showCodeContributorsWebsite() }
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
*
|
||||||
|
* 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.uhabits.activities.common.dialogs
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.GONE
|
||||||
|
import android.view.View.VISIBLE
|
||||||
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.NO
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.SKIP
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
||||||
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
|
import org.isoron.uhabits.databinding.CheckmarkPopupBinding
|
||||||
|
import org.isoron.uhabits.utils.InterfaceUtils.getFontAwesome
|
||||||
|
import org.isoron.uhabits.utils.dimBehind
|
||||||
|
import org.isoron.uhabits.utils.dp
|
||||||
|
import org.isoron.uhabits.utils.sres
|
||||||
|
|
||||||
|
const val POPUP_WIDTH = 4 * 48f + 16f
|
||||||
|
const val POPUP_HEIGHT = 48f * 2.5f + 8f
|
||||||
|
|
||||||
|
class CheckmarkPopup(
|
||||||
|
private val context: Context,
|
||||||
|
private val color: Int,
|
||||||
|
private var notes: String,
|
||||||
|
private var value: Int,
|
||||||
|
private val prefs: Preferences,
|
||||||
|
private val anchor: View,
|
||||||
|
) {
|
||||||
|
var onToggle: (Int, String) -> Unit = { _, _ -> }
|
||||||
|
private lateinit var dialog: Dialog
|
||||||
|
|
||||||
|
private val view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context)).apply {
|
||||||
|
// Required for round corners
|
||||||
|
container.clipToOutline = true
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.booleanButtons.visibility = VISIBLE
|
||||||
|
initColors()
|
||||||
|
initTypefaces()
|
||||||
|
hideDisabledButtons()
|
||||||
|
populate()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initColors() {
|
||||||
|
arrayOf(view.yesBtn, view.skipBtn).forEach {
|
||||||
|
it.setTextColor(color)
|
||||||
|
}
|
||||||
|
arrayOf(view.noBtn, view.unknownBtn).forEach {
|
||||||
|
it.setTextColor(view.root.sres.getColor(R.attr.contrast60))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initTypefaces() {
|
||||||
|
arrayOf(view.yesBtn, view.noBtn, view.skipBtn, view.unknownBtn).forEach {
|
||||||
|
it.typeface = getFontAwesome(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideDisabledButtons() {
|
||||||
|
if (!prefs.isSkipEnabled) view.skipBtn.visibility = GONE
|
||||||
|
if (!prefs.areQuestionMarksEnabled) view.unknownBtn.visibility = GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populate() {
|
||||||
|
val selectedBtn = when (value) {
|
||||||
|
YES_MANUAL -> view.yesBtn
|
||||||
|
YES_AUTO -> view.noBtn
|
||||||
|
NO -> view.noBtn
|
||||||
|
UNKNOWN -> if (prefs.areQuestionMarksEnabled) view.unknownBtn else view.noBtn
|
||||||
|
SKIP -> if (prefs.isSkipEnabled) view.skipBtn else view.noBtn
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
view.notes.setText(notes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun show() {
|
||||||
|
dialog = Dialog(context, android.R.style.Theme_NoTitleBar)
|
||||||
|
dialog.setContentView(view.root)
|
||||||
|
dialog.window?.apply {
|
||||||
|
setLayout(
|
||||||
|
view.root.dp(POPUP_WIDTH).toInt(),
|
||||||
|
view.root.dp(POPUP_HEIGHT).toInt()
|
||||||
|
)
|
||||||
|
setBackgroundDrawableResource(android.R.color.transparent)
|
||||||
|
}
|
||||||
|
fun onClick(v: Int) {
|
||||||
|
this.value = v
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
view.yesBtn.setOnClickListener { onClick(YES_MANUAL) }
|
||||||
|
view.noBtn.setOnClickListener { onClick(NO) }
|
||||||
|
view.skipBtn.setOnClickListener { onClick(SKIP) }
|
||||||
|
view.unknownBtn.setOnClickListener { onClick(UNKNOWN) }
|
||||||
|
dialog.setCanceledOnTouchOutside(true)
|
||||||
|
dialog.dimBehind()
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save() {
|
||||||
|
onToggle(value, view.notes.text.toString().trim())
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,20 +19,21 @@
|
|||||||
package org.isoron.uhabits.activities.common.dialogs
|
package org.isoron.uhabits.activities.common.dialogs
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
import org.isoron.uhabits.inject.ActivityScope
|
import org.isoron.uhabits.inject.ActivityScope
|
||||||
import org.isoron.uhabits.utils.StyledResources
|
import org.isoron.uhabits.utils.StyledResources
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ActivityScope
|
@ActivityScope
|
||||||
class ColorPickerDialogFactory @Inject constructor(@param:ActivityContext private val context: Context) {
|
class ColorPickerDialogFactory @Inject constructor(@param:ActivityContext private val context: Context) {
|
||||||
fun create(color: PaletteColor): ColorPickerDialog {
|
fun create(color: PaletteColor, theme: Theme): ColorPickerDialog {
|
||||||
val dialog = ColorPickerDialog()
|
val dialog = ColorPickerDialog()
|
||||||
val res = StyledResources(context)
|
val res = StyledResources(context)
|
||||||
val androidColor = color.toThemedAndroidColor(context)
|
val androidColor = theme.color(color).toInt()
|
||||||
dialog.initialize(
|
dialog.initialize(
|
||||||
R.string.color_picker_default_title,
|
R.string.color_picker_default_title,
|
||||||
res.getPalette(),
|
res.getPalette(),
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ package org.isoron.uhabits.activities.common.dialogs
|
|||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
@@ -44,7 +43,7 @@ class FrequencyPickerDialog(
|
|||||||
constructor() : this(1, 1)
|
constructor() : this(1, 1)
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val inflater = LayoutInflater.from(activity!!)
|
val inflater = LayoutInflater.from(requireActivity())
|
||||||
contentView = inflater.inflate(R.layout.frequency_picker_dialog, null)
|
contentView = inflater.inflate(R.layout.frequency_picker_dialog, null)
|
||||||
|
|
||||||
addBeforeAfterText(
|
addBeforeAfterText(
|
||||||
@@ -62,15 +61,19 @@ class FrequencyPickerDialog(
|
|||||||
contentView.xTimesPerMonthContainer,
|
contentView.xTimesPerMonthContainer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
addBeforeAfterText(
|
||||||
|
this.getString(R.string.x_times_per_y_days),
|
||||||
|
contentView.xTimesPerYDaysContainer,
|
||||||
|
)
|
||||||
|
|
||||||
contentView.everyDayRadioButton.setOnClickListener {
|
contentView.everyDayRadioButton.setOnClickListener {
|
||||||
check(contentView.everyDayRadioButton)
|
check(contentView.everyDayRadioButton)
|
||||||
unfocusAll()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contentView.everyXDaysRadioButton.setOnClickListener {
|
contentView.everyXDaysRadioButton.setOnClickListener {
|
||||||
check(contentView.everyXDaysRadioButton)
|
check(contentView.everyXDaysRadioButton)
|
||||||
val everyXDaysTextView = contentView.everyXDaysTextView
|
val everyXDaysTextView = contentView.everyXDaysTextView
|
||||||
focus(everyXDaysTextView)
|
selectInputField(everyXDaysTextView)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentView.everyXDaysTextView.setOnFocusChangeListener { v, hasFocus ->
|
contentView.everyXDaysTextView.setOnFocusChangeListener { v, hasFocus ->
|
||||||
@@ -79,7 +82,7 @@ class FrequencyPickerDialog(
|
|||||||
|
|
||||||
contentView.xTimesPerWeekRadioButton.setOnClickListener {
|
contentView.xTimesPerWeekRadioButton.setOnClickListener {
|
||||||
check(contentView.xTimesPerWeekRadioButton)
|
check(contentView.xTimesPerWeekRadioButton)
|
||||||
focus(contentView.xTimesPerWeekTextView)
|
selectInputField(contentView.xTimesPerWeekTextView)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentView.xTimesPerWeekTextView.setOnFocusChangeListener { v, hasFocus ->
|
contentView.xTimesPerWeekTextView.setOnFocusChangeListener { v, hasFocus ->
|
||||||
@@ -88,14 +91,27 @@ class FrequencyPickerDialog(
|
|||||||
|
|
||||||
contentView.xTimesPerMonthRadioButton.setOnClickListener {
|
contentView.xTimesPerMonthRadioButton.setOnClickListener {
|
||||||
check(contentView.xTimesPerMonthRadioButton)
|
check(contentView.xTimesPerMonthRadioButton)
|
||||||
focus(contentView.xTimesPerMonthTextView)
|
selectInputField(contentView.xTimesPerMonthTextView)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentView.xTimesPerMonthTextView.setOnFocusChangeListener { v, hasFocus ->
|
contentView.xTimesPerMonthTextView.setOnFocusChangeListener { v, hasFocus ->
|
||||||
if (hasFocus) check(contentView.xTimesPerMonthRadioButton)
|
if (hasFocus) check(contentView.xTimesPerMonthRadioButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
return AlertDialog.Builder(activity!!)
|
contentView.xTimesPerYDaysRadioButton.setOnClickListener {
|
||||||
|
check(contentView.xTimesPerYDaysRadioButton)
|
||||||
|
selectInputField(contentView.xTimesPerYDaysXTextView)
|
||||||
|
}
|
||||||
|
|
||||||
|
contentView.xTimesPerYDaysXTextView.setOnFocusChangeListener { v, hasFocus ->
|
||||||
|
if (hasFocus) check(contentView.xTimesPerYDaysRadioButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
contentView.xTimesPerYDaysYTextView.setOnFocusChangeListener { v, hasFocus ->
|
||||||
|
if (hasFocus) check(contentView.xTimesPerYDaysRadioButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
return AlertDialog.Builder(requireActivity())
|
||||||
.setView(contentView)
|
.setView(contentView)
|
||||||
.setPositiveButton(R.string.save) { _, _ -> onSaveClicked() }
|
.setPositiveButton(R.string.save) { _, _ -> onSaveClicked() }
|
||||||
.create()
|
.create()
|
||||||
@@ -106,12 +122,11 @@ class FrequencyPickerDialog(
|
|||||||
container: LinearLayout
|
container: LinearLayout
|
||||||
) {
|
) {
|
||||||
val parts = str.split("%d")
|
val parts = str.split("%d")
|
||||||
container.addView(
|
for (i in parts.indices) {
|
||||||
TextView(activity).apply { text = parts[0].trim() }, 1,
|
container.addView(
|
||||||
)
|
TextView(activity).apply { text = parts[i].trim() }, 2 * i + 1,
|
||||||
container.addView(
|
)
|
||||||
TextView(activity).apply { text = parts[1].trim() }, 3,
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onSaveClicked() {
|
private fun onSaveClicked() {
|
||||||
@@ -132,6 +147,12 @@ class FrequencyPickerDialog(
|
|||||||
denominator = 7
|
denominator = 7
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
contentView.xTimesPerYDaysRadioButton.isChecked -> {
|
||||||
|
if (contentView.xTimesPerYDaysXTextView.text.isNotEmpty() && contentView.xTimesPerYDaysYTextView.text.isNotEmpty()) {
|
||||||
|
numerator = Integer.parseInt(contentView.xTimesPerYDaysXTextView.text.toString())
|
||||||
|
denominator = Integer.parseInt(contentView.xTimesPerYDaysYTextView.text.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if (contentView.xTimesPerMonthTextView.text.isNotEmpty()) {
|
if (contentView.xTimesPerMonthTextView.text.isNotEmpty()) {
|
||||||
numerator = Integer.parseInt(contentView.xTimesPerMonthTextView.text.toString())
|
numerator = Integer.parseInt(contentView.xTimesPerMonthTextView.text.toString())
|
||||||
@@ -147,10 +168,10 @@ class FrequencyPickerDialog(
|
|||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun check(view: RadioButton?) {
|
private fun check(view: RadioButton) {
|
||||||
uncheckAll()
|
uncheckAll()
|
||||||
view?.isChecked = true
|
view.isChecked = true
|
||||||
view?.requestFocus()
|
view.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
@@ -160,32 +181,34 @@ class FrequencyPickerDialog(
|
|||||||
|
|
||||||
private fun populateViews() {
|
private fun populateViews() {
|
||||||
uncheckAll()
|
uncheckAll()
|
||||||
if (freqNumerator == 1) {
|
if (freqDenominator == 30 || freqDenominator == 31) {
|
||||||
if (freqDenominator == 1) {
|
contentView.xTimesPerMonthRadioButton.isChecked = true
|
||||||
contentView.everyDayRadioButton.isChecked = true
|
contentView.xTimesPerMonthTextView.setText(freqNumerator.toString())
|
||||||
} else {
|
selectInputField(contentView.xTimesPerMonthTextView)
|
||||||
contentView.everyXDaysRadioButton.isChecked = true
|
|
||||||
contentView.everyXDaysTextView.setText(freqDenominator.toString())
|
|
||||||
focus(contentView.everyXDaysTextView)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (freqDenominator == 7) {
|
if (freqNumerator == 1) {
|
||||||
contentView.xTimesPerWeekRadioButton.isChecked = true
|
if (freqDenominator == 1) {
|
||||||
contentView.xTimesPerWeekTextView.setText(freqNumerator.toString())
|
contentView.everyDayRadioButton.isChecked = true
|
||||||
focus(contentView.xTimesPerWeekTextView)
|
} else {
|
||||||
} else if (freqDenominator == 30 || freqDenominator == 31) {
|
contentView.everyXDaysRadioButton.isChecked = true
|
||||||
contentView.xTimesPerMonthRadioButton.isChecked = true
|
contentView.everyXDaysTextView.setText(freqDenominator.toString())
|
||||||
contentView.xTimesPerMonthTextView.setText(freqNumerator.toString())
|
selectInputField(contentView.everyXDaysTextView)
|
||||||
focus(contentView.xTimesPerMonthTextView)
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.w("FrequencyPickerDialog", "Unknown frequency: $freqNumerator/$freqDenominator")
|
if (freqDenominator == 7) {
|
||||||
contentView.everyDayRadioButton.isChecked = true
|
contentView.xTimesPerWeekRadioButton.isChecked = true
|
||||||
|
contentView.xTimesPerWeekTextView.setText(freqNumerator.toString())
|
||||||
|
selectInputField(contentView.xTimesPerWeekTextView)
|
||||||
|
} else {
|
||||||
|
contentView.xTimesPerYDaysRadioButton.isChecked = true
|
||||||
|
contentView.xTimesPerYDaysXTextView.setText(freqNumerator.toString())
|
||||||
|
contentView.xTimesPerYDaysYTextView.setText(freqDenominator.toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun focus(view: EditText) {
|
private fun selectInputField(view: EditText) {
|
||||||
view.requestFocus()
|
|
||||||
view.setSelection(view.text.length)
|
view.setSelection(view.text.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,11 +217,6 @@ class FrequencyPickerDialog(
|
|||||||
contentView.everyXDaysRadioButton.isChecked = false
|
contentView.everyXDaysRadioButton.isChecked = false
|
||||||
contentView.xTimesPerWeekRadioButton.isChecked = false
|
contentView.xTimesPerWeekRadioButton.isChecked = false
|
||||||
contentView.xTimesPerMonthRadioButton.isChecked = false
|
contentView.xTimesPerMonthRadioButton.isChecked = false
|
||||||
}
|
contentView.xTimesPerYDaysRadioButton.isChecked = false
|
||||||
|
|
||||||
private fun unfocusAll() {
|
|
||||||
contentView.everyXDaysTextView.clearFocus()
|
|
||||||
contentView.xTimesPerWeekTextView.clearFocus()
|
|
||||||
contentView.xTimesPerMonthTextView.clearFocus()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener {
|
|||||||
private lateinit var commandRunner: CommandRunner
|
private lateinit var commandRunner: CommandRunner
|
||||||
private lateinit var habit: Habit
|
private lateinit var habit: Habit
|
||||||
private lateinit var preferences: Preferences
|
private lateinit var preferences: Preferences
|
||||||
private lateinit var dataView: AndroidDataView
|
lateinit var dataView: AndroidDataView
|
||||||
|
|
||||||
private var chart: HistoryChart? = null
|
private var chart: HistoryChart? = null
|
||||||
private var onDateClickedListener: OnDateClickedListener? = null
|
private var onDateClickedListener: OnDateClickedListener? = null
|
||||||
@@ -62,9 +62,11 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener {
|
|||||||
firstWeekday = preferences.firstWeekday,
|
firstWeekday = preferences.firstWeekday,
|
||||||
paletteColor = habit.color,
|
paletteColor = habit.color,
|
||||||
series = emptyList(),
|
series = emptyList(),
|
||||||
|
defaultSquare = HistoryChart.Square.OFF,
|
||||||
|
notesIndicators = emptyList(),
|
||||||
theme = themeSwitcher.currentTheme,
|
theme = themeSwitcher.currentTheme,
|
||||||
today = DateUtils.getTodayWithOffset().toLocalDate(),
|
today = DateUtils.getTodayWithOffset().toLocalDate(),
|
||||||
onDateClickedListener = onDateClickedListener ?: OnDateClickedListener { },
|
onDateClickedListener = onDateClickedListener ?: object : OnDateClickedListener {},
|
||||||
padding = 10.0,
|
padding = 10.0,
|
||||||
)
|
)
|
||||||
dataView = AndroidDataView(context!!, null)
|
dataView = AndroidDataView(context!!, null)
|
||||||
@@ -101,6 +103,8 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener {
|
|||||||
theme = LightTheme()
|
theme = LightTheme()
|
||||||
)
|
)
|
||||||
chart?.series = model.series
|
chart?.series = model.series
|
||||||
|
chart?.defaultSquare = model.defaultSquare
|
||||||
|
chart?.notesIndicators = model.notesIndicators
|
||||||
dataView.postInvalidate()
|
dataView.postInvalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
|
||||||
*
|
|
||||||
* 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.uhabits.activities.common.dialogs
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.text.InputFilter
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
|
|
||||||
import android.view.inputmethod.EditorInfo
|
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.NumberPicker
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import org.isoron.uhabits.R
|
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils
|
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlin.math.roundToLong
|
|
||||||
|
|
||||||
class NumberPickerFactory
|
|
||||||
@Inject constructor(
|
|
||||||
@ActivityContext private val context: Context
|
|
||||||
) {
|
|
||||||
fun create(
|
|
||||||
value: Double,
|
|
||||||
unit: String,
|
|
||||||
callback: ListHabitsBehavior.NumberPickerCallback
|
|
||||||
): AlertDialog {
|
|
||||||
|
|
||||||
val inflater = LayoutInflater.from(context)
|
|
||||||
val view = inflater.inflate(R.layout.number_picker_dialog, null)
|
|
||||||
|
|
||||||
val picker = view.findViewById<NumberPicker>(R.id.picker)
|
|
||||||
val picker2 = view.findViewById<NumberPicker>(R.id.picker2)
|
|
||||||
val tvUnit = view.findViewById<TextView>(R.id.tvUnit)
|
|
||||||
|
|
||||||
val intValue = (value * 100).roundToLong().toInt()
|
|
||||||
|
|
||||||
picker.minValue = 0
|
|
||||||
picker.maxValue = Integer.MAX_VALUE / 100
|
|
||||||
picker.value = intValue / 100
|
|
||||||
picker.wrapSelectorWheel = false
|
|
||||||
|
|
||||||
picker2.minValue = 0
|
|
||||||
picker2.maxValue = 19
|
|
||||||
picker2.setFormatter { v -> String.format("%02d", 5 * v) }
|
|
||||||
picker2.value = intValue % 100 / 5
|
|
||||||
refreshInitialValue(picker2)
|
|
||||||
|
|
||||||
tvUnit.text = unit
|
|
||||||
|
|
||||||
val dialog = AlertDialog.Builder(context)
|
|
||||||
.setView(view)
|
|
||||||
.setTitle(R.string.change_value)
|
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
|
||||||
picker.clearFocus()
|
|
||||||
val v = picker.value + 0.05 * picker2.value
|
|
||||||
callback.onNumberPicked(v)
|
|
||||||
}
|
|
||||||
.setOnDismissListener {
|
|
||||||
callback.onNumberPickerDismissed()
|
|
||||||
}
|
|
||||||
.create()
|
|
||||||
|
|
||||||
dialog.setOnShowListener {
|
|
||||||
picker.getChildAt(0)?.requestFocus()
|
|
||||||
dialog.window?.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
|
||||||
}
|
|
||||||
|
|
||||||
InterfaceUtils.setupEditorAction(
|
|
||||||
picker
|
|
||||||
) { _, actionId, _ ->
|
|
||||||
if (actionId == EditorInfo.IME_ACTION_DONE)
|
|
||||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
return dialog
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun refreshInitialValue(picker: NumberPicker) {
|
|
||||||
// Workaround for Android bug:
|
|
||||||
// https://code.google.com/p/android/issues/detail?id=35482
|
|
||||||
val f = NumberPicker::class.java.getDeclaredField("mInputText")
|
|
||||||
f.isAccessible = true
|
|
||||||
val inputText = f.get(picker) as EditText
|
|
||||||
inputText.filters = arrayOfNulls<InputFilter>(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
*
|
||||||
|
* 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.uhabits.activities.common.dialogs
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.KeyEvent.KEYCODE_ENTER
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MotionEvent.ACTION_DOWN
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.GONE
|
||||||
|
import android.view.View.VISIBLE
|
||||||
|
import org.isoron.uhabits.core.models.Entry
|
||||||
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
|
import org.isoron.uhabits.databinding.CheckmarkPopupBinding
|
||||||
|
import org.isoron.uhabits.utils.dimBehind
|
||||||
|
import org.isoron.uhabits.utils.dp
|
||||||
|
import org.isoron.uhabits.utils.requestFocusWithKeyboard
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
|
||||||
|
class NumberPopup(
|
||||||
|
private val context: Context,
|
||||||
|
private var notes: String,
|
||||||
|
private var value: Double,
|
||||||
|
private val prefs: Preferences,
|
||||||
|
private val anchor: View,
|
||||||
|
) {
|
||||||
|
var onToggle: (Double, String) -> Unit = { _, _ -> }
|
||||||
|
private val originalValue = value
|
||||||
|
private lateinit var dialog: Dialog
|
||||||
|
|
||||||
|
private val view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context)).apply {
|
||||||
|
// Required for round corners
|
||||||
|
container.clipToOutline = true
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.numberButtons.visibility = VISIBLE
|
||||||
|
hideDisabledButtons()
|
||||||
|
populate()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideDisabledButtons() {
|
||||||
|
if (!prefs.isSkipEnabled) view.skipBtnNumber.visibility = GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populate() {
|
||||||
|
view.notes.setText(notes)
|
||||||
|
view.value.setText(
|
||||||
|
when {
|
||||||
|
value < 0.01 -> "0"
|
||||||
|
else -> DecimalFormat("#.##").format(value)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun show() {
|
||||||
|
dialog = Dialog(context, android.R.style.Theme_NoTitleBar)
|
||||||
|
dialog.setContentView(view.root)
|
||||||
|
dialog.window?.apply {
|
||||||
|
setLayout(
|
||||||
|
view.root.dp(POPUP_WIDTH).toInt(),
|
||||||
|
view.root.dp(POPUP_HEIGHT).toInt()
|
||||||
|
)
|
||||||
|
setBackgroundDrawableResource(android.R.color.transparent)
|
||||||
|
}
|
||||||
|
|
||||||
|
view.value.setOnKeyListener { _, keyCode, event ->
|
||||||
|
if (event.action == ACTION_DOWN && keyCode == KEYCODE_ENTER) {
|
||||||
|
save()
|
||||||
|
return@setOnKeyListener true
|
||||||
|
}
|
||||||
|
return@setOnKeyListener false
|
||||||
|
}
|
||||||
|
view.saveBtn.setOnClickListener {
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
view.skipBtnNumber.setOnClickListener {
|
||||||
|
view.value.setText((Entry.SKIP.toDouble() / 1000).toString())
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
view.value.requestFocusWithKeyboard()
|
||||||
|
dialog.setCanceledOnTouchOutside(true)
|
||||||
|
dialog.dimBehind()
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save() {
|
||||||
|
val value = view.value.text.toString().toDoubleOrNull() ?: originalValue
|
||||||
|
val notes = view.notes.text.toString()
|
||||||
|
onToggle(value, notes)
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ import org.isoron.uhabits.core.utils.DateUtils.Companion.getShortWeekdayNames
|
|||||||
import org.isoron.uhabits.core.utils.DateUtils.Companion.getStartOfTodayCalendar
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getStartOfTodayCalendar
|
||||||
import org.isoron.uhabits.core.utils.DateUtils.Companion.getStartOfTodayCalendarWithOffset
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getStartOfTodayCalendarWithOffset
|
||||||
import org.isoron.uhabits.core.utils.DateUtils.Companion.getWeekdaySequence
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getWeekdaySequence
|
||||||
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getWeekdaysInMonth
|
||||||
import org.isoron.uhabits.utils.ColorUtils.mixColors
|
import org.isoron.uhabits.utils.ColorUtils.mixColors
|
||||||
import org.isoron.uhabits.utils.StyledResources
|
import org.isoron.uhabits.utils.StyledResources
|
||||||
import org.isoron.uhabits.utils.toSimpleDataFormat
|
import org.isoron.uhabits.utils.toSimpleDataFormat
|
||||||
@@ -62,7 +63,6 @@ class FrequencyChart : ScrollableChart {
|
|||||||
private var primaryColor = 0
|
private var primaryColor = 0
|
||||||
private var isBackgroundTransparent = false
|
private var isBackgroundTransparent = false
|
||||||
private lateinit var frequency: HashMap<Timestamp, Array<Int>>
|
private lateinit var frequency: HashMap<Timestamp, Array<Int>>
|
||||||
private var maxFreq = 0
|
|
||||||
private var firstWeekday = Calendar.SUNDAY
|
private var firstWeekday = Calendar.SUNDAY
|
||||||
|
|
||||||
constructor(context: Context?) : super(context) {
|
constructor(context: Context?) : super(context) {
|
||||||
@@ -82,7 +82,6 @@ class FrequencyChart : ScrollableChart {
|
|||||||
|
|
||||||
fun setFrequency(frequency: java.util.HashMap<Timestamp, Array<Int>>) {
|
fun setFrequency(frequency: java.util.HashMap<Timestamp, Array<Int>>) {
|
||||||
this.frequency = frequency
|
this.frequency = frequency
|
||||||
maxFreq = getMaxFreq(frequency)
|
|
||||||
postInvalidate()
|
postInvalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,15 +90,6 @@ class FrequencyChart : ScrollableChart {
|
|||||||
postInvalidate()
|
postInvalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMaxFreq(frequency: HashMap<Timestamp, Array<Int>>): Int {
|
|
||||||
var maxValue = 1
|
|
||||||
for (values in frequency.values) for (value in values) maxValue = max(
|
|
||||||
value,
|
|
||||||
maxValue
|
|
||||||
)
|
|
||||||
return maxValue
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setIsBackgroundTransparent(isBackgroundTransparent: Boolean) {
|
fun setIsBackgroundTransparent(isBackgroundTransparent: Boolean) {
|
||||||
this.isBackgroundTransparent = isBackgroundTransparent
|
this.isBackgroundTransparent = isBackgroundTransparent
|
||||||
initColors()
|
initColors()
|
||||||
@@ -166,6 +156,7 @@ class FrequencyChart : ScrollableChart {
|
|||||||
|
|
||||||
private fun drawColumn(canvas: Canvas, rect: RectF?, date: GregorianCalendar) {
|
private fun drawColumn(canvas: Canvas, rect: RectF?, date: GregorianCalendar) {
|
||||||
val values = frequency[Timestamp(date)]
|
val values = frequency[Timestamp(date)]
|
||||||
|
val weekDaysInMonth = getWeekdaysInMonth(Timestamp(date))
|
||||||
val rowHeight = rect!!.height() / 8.0f
|
val rowHeight = rect!!.height() / 8.0f
|
||||||
prevRect!!.set(rect)
|
prevRect!!.set(rect)
|
||||||
val localeWeekdayList: Array<Int> = getWeekdaySequence(firstWeekday)
|
val localeWeekdayList: Array<Int> = getWeekdaySequence(firstWeekday)
|
||||||
@@ -173,7 +164,8 @@ class FrequencyChart : ScrollableChart {
|
|||||||
rect[0f, 0f, baseSize.toFloat()] = baseSize.toFloat()
|
rect[0f, 0f, baseSize.toFloat()] = baseSize.toFloat()
|
||||||
rect.offset(prevRect!!.left, prevRect!!.top + baseSize * j)
|
rect.offset(prevRect!!.left, prevRect!!.top + baseSize * j)
|
||||||
val i = localeWeekdayList[j] % 7
|
val i = localeWeekdayList[j] % 7
|
||||||
if (values != null) drawMarker(canvas, rect, values[i])
|
if (values != null)
|
||||||
|
drawMarker(canvas, rect, values[i], weekDaysInMonth[i])
|
||||||
rect.offset(0f, rowHeight)
|
rect.offset(0f, rowHeight)
|
||||||
}
|
}
|
||||||
drawFooter(canvas, rect, date)
|
drawFooter(canvas, rect, date)
|
||||||
@@ -221,12 +213,16 @@ class FrequencyChart : ScrollableChart {
|
|||||||
canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid!!)
|
canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun drawMarker(canvas: Canvas, rect: RectF?, value: Int?) {
|
private fun drawMarker(canvas: Canvas, rect: RectF?, value: Int?, frequency: Int) {
|
||||||
|
// value can be negative when the entry is skipped
|
||||||
|
val valueCopy = value?.let { max(0, it) }
|
||||||
|
|
||||||
val padding = rect!!.height() * 0.2f
|
val padding = rect!!.height() * 0.2f
|
||||||
// maximal allowed mark radius
|
// maximal allowed mark radius
|
||||||
val maxRadius = (rect.height() - 2 * padding) / 2.0f
|
val maxRadius = (rect.height() - 2 * padding) / 2.0f
|
||||||
// the real mark radius is scaled down by a factor depending on the maximal frequency
|
// the real mark radius is scaled down by a factor depending on the maximal frequency
|
||||||
val scale = 1.0f / maxFreq * value!!
|
|
||||||
|
val scale = 1.0f / frequency * valueCopy!!
|
||||||
val radius = maxRadius * scale
|
val radius = maxRadius * scale
|
||||||
val colorIndex = min((colors.size - 1), ((colors.size - 1) * scale).roundToInt())
|
val colorIndex = min((colors.size - 1), ((colors.size - 1) * scale).roundToInt())
|
||||||
pGraph!!.color = colors[colorIndex]
|
pGraph!!.color = colors[colorIndex]
|
||||||
@@ -289,6 +285,5 @@ class FrequencyChart : ScrollableChart {
|
|||||||
frequency[Timestamp(date)] = values
|
frequency[Timestamp(date)] = values
|
||||||
date.add(Calendar.MONTH, -1)
|
date.add(Calendar.MONTH, -1)
|
||||||
}
|
}
|
||||||
maxFreq = getMaxFreq(frequency)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ class TaskProgressBar(
|
|||||||
context: Context,
|
context: Context,
|
||||||
private val runner: TaskRunner
|
private val runner: TaskRunner
|
||||||
) : ProgressBar(
|
) : ProgressBar(
|
||||||
context,
|
context,
|
||||||
null,
|
null,
|
||||||
android.R.attr.progressBarStyleHorizontal
|
android.R.attr.progressBarStyleHorizontal
|
||||||
),
|
),
|
||||||
TaskRunner.Listener {
|
TaskRunner.Listener {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ package org.isoron.uhabits.activities.habits.edit
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
|
import android.content.res.Resources
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
@@ -39,6 +40,7 @@ import kotlinx.android.synthetic.main.activity_edit_habit.notesInput
|
|||||||
import kotlinx.android.synthetic.main.activity_edit_habit.questionInput
|
import kotlinx.android.synthetic.main.activity_edit_habit.questionInput
|
||||||
import kotlinx.android.synthetic.main.activity_edit_habit.targetInput
|
import kotlinx.android.synthetic.main.activity_edit_habit.targetInput
|
||||||
import kotlinx.android.synthetic.main.activity_edit_habit.unitInput
|
import kotlinx.android.synthetic.main.activity_edit_habit.unitInput
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
@@ -50,6 +52,8 @@ import org.isoron.uhabits.core.commands.CreateHabitCommand
|
|||||||
import org.isoron.uhabits.core.commands.EditHabitCommand
|
import org.isoron.uhabits.core.commands.EditHabitCommand
|
||||||
import org.isoron.uhabits.core.models.Frequency
|
import org.isoron.uhabits.core.models.Frequency
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
|
import org.isoron.uhabits.core.models.HabitType
|
||||||
|
import org.isoron.uhabits.core.models.NumericalHabitType
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.models.Reminder
|
import org.isoron.uhabits.core.models.Reminder
|
||||||
import org.isoron.uhabits.core.models.WeekdayList
|
import org.isoron.uhabits.core.models.WeekdayList
|
||||||
@@ -57,7 +61,16 @@ import org.isoron.uhabits.databinding.ActivityEditHabitBinding
|
|||||||
import org.isoron.uhabits.utils.ColorUtils
|
import org.isoron.uhabits.utils.ColorUtils
|
||||||
import org.isoron.uhabits.utils.formatTime
|
import org.isoron.uhabits.utils.formatTime
|
||||||
import org.isoron.uhabits.utils.toFormattedString
|
import org.isoron.uhabits.utils.toFormattedString
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
|
fun formatFrequency(freqNum: Int, freqDen: Int, resources: Resources) = when {
|
||||||
|
freqNum == 1 && (freqDen == 30 || freqDen == 31) -> resources.getString(R.string.every_month)
|
||||||
|
freqDen == 30 || freqDen == 31 -> resources.getString(R.string.x_times_per_month, freqNum)
|
||||||
|
freqNum == 1 && freqDen == 1 -> resources.getString(R.string.every_day)
|
||||||
|
freqNum == 1 && freqDen == 7 -> resources.getString(R.string.every_week)
|
||||||
|
freqNum == 1 && freqDen > 1 -> resources.getString(R.string.every_x_days, freqDen)
|
||||||
|
freqDen == 7 -> resources.getString(R.string.x_times_per_week, freqNum)
|
||||||
|
else -> resources.getString(R.string.x_times_per_y_days, freqNum, freqDen)
|
||||||
|
}
|
||||||
|
|
||||||
class EditHabitActivity : AppCompatActivity() {
|
class EditHabitActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@@ -66,7 +79,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
private lateinit var commandRunner: CommandRunner
|
private lateinit var commandRunner: CommandRunner
|
||||||
|
|
||||||
var habitId = -1L
|
var habitId = -1L
|
||||||
var habitType = -1
|
lateinit var habitType: HabitType
|
||||||
var unit = ""
|
var unit = ""
|
||||||
var color = PaletteColor(11)
|
var color = PaletteColor(11)
|
||||||
var androidColor = 0
|
var androidColor = 0
|
||||||
@@ -75,6 +88,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
var reminderHour = -1
|
var reminderHour = -1
|
||||||
var reminderMin = -1
|
var reminderMin = -1
|
||||||
var reminderDays: WeekdayList = WeekdayList.EVERY_DAY
|
var reminderDays: WeekdayList = WeekdayList.EVERY_DAY
|
||||||
|
var targetType = NumericalHabitType.AT_LEAST
|
||||||
|
|
||||||
override fun onCreate(state: Bundle?) {
|
override fun onCreate(state: Bundle?) {
|
||||||
super.onCreate(state)
|
super.onCreate(state)
|
||||||
@@ -94,6 +108,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
color = habit.color
|
color = habit.color
|
||||||
freqNum = habit.frequency.numerator
|
freqNum = habit.frequency.numerator
|
||||||
freqDen = habit.frequency.denominator
|
freqDen = habit.frequency.denominator
|
||||||
|
targetType = habit.targetType
|
||||||
habit.reminder?.let {
|
habit.reminder?.let {
|
||||||
reminderHour = it.hour
|
reminderHour = it.hour
|
||||||
reminderMin = it.minute
|
reminderMin = it.minute
|
||||||
@@ -105,12 +120,12 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
binding.unitInput.setText(habit.unit)
|
binding.unitInput.setText(habit.unit)
|
||||||
binding.targetInput.setText(habit.targetValue.toString())
|
binding.targetInput.setText(habit.targetValue.toString())
|
||||||
} else {
|
} else {
|
||||||
habitType = intent.getIntExtra("habitType", Habit.YES_NO_HABIT)
|
habitType = HabitType.fromInt(intent.getIntExtra("habitType", HabitType.YES_NO.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state != null) {
|
if (state != null) {
|
||||||
habitId = state.getLong("habitId")
|
habitId = state.getLong("habitId")
|
||||||
habitType = state.getInt("habitType")
|
habitType = HabitType.fromInt(state.getInt("habitType"))
|
||||||
color = PaletteColor(state.getInt("paletteColor"))
|
color = PaletteColor(state.getInt("paletteColor"))
|
||||||
freqNum = state.getInt("freqNum")
|
freqNum = state.getInt("freqNum")
|
||||||
freqDen = state.getInt("freqDen")
|
freqDen = state.getInt("freqDen")
|
||||||
@@ -121,13 +136,17 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
updateColors()
|
updateColors()
|
||||||
|
|
||||||
if (habitType == Habit.YES_NO_HABIT) {
|
when (habitType) {
|
||||||
binding.unitOuterBox.visibility = View.GONE
|
HabitType.YES_NO -> {
|
||||||
binding.targetOuterBox.visibility = View.GONE
|
binding.unitOuterBox.visibility = View.GONE
|
||||||
} else {
|
binding.targetOuterBox.visibility = View.GONE
|
||||||
binding.nameInput.hint = getString(R.string.measurable_short_example)
|
binding.targetTypeOuterBox.visibility = View.GONE
|
||||||
binding.questionInput.hint = getString(R.string.measurable_question_example)
|
}
|
||||||
binding.frequencyOuterBox.visibility = View.GONE
|
HabitType.NUMERICAL -> {
|
||||||
|
binding.nameInput.hint = getString(R.string.measurable_short_example)
|
||||||
|
binding.questionInput.hint = getString(R.string.measurable_question_example)
|
||||||
|
binding.frequencyOuterBox.visibility = View.GONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSupportActionBar(binding.toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
@@ -137,7 +156,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
val colorPickerDialogFactory = ColorPickerDialogFactory(this)
|
val colorPickerDialogFactory = ColorPickerDialogFactory(this)
|
||||||
binding.colorButton.setOnClickListener {
|
binding.colorButton.setOnClickListener {
|
||||||
val dialog = colorPickerDialogFactory.create(color)
|
val dialog = colorPickerDialogFactory.create(color, themeSwitcher.currentTheme)
|
||||||
dialog.setListener { paletteColor ->
|
dialog.setListener { paletteColor ->
|
||||||
this.color = paletteColor
|
this.color = paletteColor
|
||||||
updateColors()
|
updateColors()
|
||||||
@@ -156,6 +175,23 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
dialog.show(supportFragmentManager, "frequencyPicker")
|
dialog.show(supportFragmentManager, "frequencyPicker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
populateTargetType()
|
||||||
|
binding.targetTypePicker.setOnClickListener {
|
||||||
|
val builder = AlertDialog.Builder(this)
|
||||||
|
val arrayAdapter = ArrayAdapter<String>(this, android.R.layout.select_dialog_item)
|
||||||
|
arrayAdapter.add(getString(R.string.target_type_at_least))
|
||||||
|
arrayAdapter.add(getString(R.string.target_type_at_most))
|
||||||
|
builder.setAdapter(arrayAdapter) { dialog, which ->
|
||||||
|
targetType = when (which) {
|
||||||
|
0 -> NumericalHabitType.AT_LEAST
|
||||||
|
else -> NumericalHabitType.AT_MOST
|
||||||
|
}
|
||||||
|
populateTargetType()
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
builder.show()
|
||||||
|
}
|
||||||
|
|
||||||
binding.numericalFrequencyPicker.setOnClickListener {
|
binding.numericalFrequencyPicker.setOnClickListener {
|
||||||
val builder = AlertDialog.Builder(this)
|
val builder = AlertDialog.Builder(this)
|
||||||
val arrayAdapter = ArrayAdapter<String>(this, android.R.layout.select_dialog_item)
|
val arrayAdapter = ArrayAdapter<String>(this, android.R.layout.select_dialog_item)
|
||||||
@@ -244,9 +280,9 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
habit.frequency = Frequency(freqNum, freqDen)
|
habit.frequency = Frequency(freqNum, freqDen)
|
||||||
if (habitType == Habit.NUMBER_HABIT) {
|
if (habitType == HabitType.NUMERICAL) {
|
||||||
habit.targetValue = targetInput.text.toString().toDouble()
|
habit.targetValue = targetInput.text.toString().toDouble()
|
||||||
habit.targetType = Habit.AT_LEAST
|
habit.targetType = targetType
|
||||||
habit.unit = unitInput.text.trim().toString()
|
habit.unit = unitInput.text.trim().toString()
|
||||||
}
|
}
|
||||||
habit.type = habitType
|
habit.type = habitType
|
||||||
@@ -274,7 +310,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
nameInput.error = getFormattedValidationError(R.string.validation_cannot_be_blank)
|
nameInput.error = getFormattedValidationError(R.string.validation_cannot_be_blank)
|
||||||
isValid = false
|
isValid = false
|
||||||
}
|
}
|
||||||
if (habitType == Habit.NUMBER_HABIT) {
|
if (habitType == HabitType.NUMERICAL) {
|
||||||
if (targetInput.text.isEmpty()) {
|
if (targetInput.text.isEmpty()) {
|
||||||
targetInput.error = getString(R.string.validation_cannot_be_blank)
|
targetInput.error = getString(R.string.validation_cannot_be_blank)
|
||||||
isValid = false
|
isValid = false
|
||||||
@@ -299,14 +335,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
@SuppressLint("StringFormatMatches")
|
@SuppressLint("StringFormatMatches")
|
||||||
private fun populateFrequency() {
|
private fun populateFrequency() {
|
||||||
binding.booleanFrequencyPicker.text = when {
|
binding.booleanFrequencyPicker.text = formatFrequency(freqNum, freqDen, resources)
|
||||||
freqNum == 1 && freqDen == 1 -> getString(R.string.every_day)
|
|
||||||
freqNum == 1 && freqDen == 7 -> getString(R.string.every_week)
|
|
||||||
freqNum == 1 && freqDen > 1 -> getString(R.string.every_x_days, freqDen)
|
|
||||||
freqDen == 7 -> getString(R.string.x_times_per_week, freqNum)
|
|
||||||
freqDen == 30 || freqDen == 31 -> getString(R.string.x_times_per_month, freqNum)
|
|
||||||
else -> "$freqNum/$freqDen"
|
|
||||||
}
|
|
||||||
binding.numericalFrequencyPicker.text = when (freqDen) {
|
binding.numericalFrequencyPicker.text = when (freqDen) {
|
||||||
1 -> getString(R.string.every_day)
|
1 -> getString(R.string.every_day)
|
||||||
7 -> getString(R.string.every_week)
|
7 -> getString(R.string.every_week)
|
||||||
@@ -315,8 +344,15 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun populateTargetType() {
|
||||||
|
binding.targetTypePicker.text = when (targetType) {
|
||||||
|
NumericalHabitType.AT_MOST -> getString(R.string.target_type_at_most)
|
||||||
|
else -> getString(R.string.target_type_at_least)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateColors() {
|
private fun updateColors() {
|
||||||
androidColor = color.toThemedAndroidColor(this)
|
androidColor = themeSwitcher.currentTheme.color(color).toInt()
|
||||||
binding.colorButton.backgroundTintList = ColorStateList.valueOf(androidColor)
|
binding.colorButton.backgroundTintList = ColorStateList.valueOf(androidColor)
|
||||||
if (!themeSwitcher.isNightMode) {
|
if (!themeSwitcher.isNightMode) {
|
||||||
val darkerAndroidColor = ColorUtils.mixColors(Color.BLACK, androidColor, 0.15f)
|
val darkerAndroidColor = ColorUtils.mixColors(Color.BLACK, androidColor, 0.15f)
|
||||||
@@ -334,7 +370,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
super.onSaveInstanceState(state)
|
super.onSaveInstanceState(state)
|
||||||
with(state) {
|
with(state) {
|
||||||
putLong("habitId", habitId)
|
putLong("habitId", habitId)
|
||||||
putInt("habitType", habitType)
|
putInt("habitType", habitType.value)
|
||||||
putInt("paletteColor", color.paletteIndex)
|
putInt("paletteColor", color.paletteIndex)
|
||||||
putInt("androidColor", androidColor)
|
putInt("androidColor", androidColor)
|
||||||
putInt("freqNum", freqNum)
|
putInt("freqNum", freqNum)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatDialogFragment
|
import androidx.appcompat.app.AppCompatDialogFragment
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.HabitType
|
||||||
import org.isoron.uhabits.databinding.SelectHabitTypeBinding
|
import org.isoron.uhabits.databinding.SelectHabitTypeBinding
|
||||||
import org.isoron.uhabits.intents.IntentFactory
|
import org.isoron.uhabits.intents.IntentFactory
|
||||||
|
|
||||||
@@ -40,13 +40,13 @@ class HabitTypeDialog : AppCompatDialogFragment() {
|
|||||||
val binding = SelectHabitTypeBinding.inflate(inflater, container, false)
|
val binding = SelectHabitTypeBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
binding.buttonYesNo.setOnClickListener {
|
binding.buttonYesNo.setOnClickListener {
|
||||||
val intent = IntentFactory().startEditActivity(activity!!, Habit.YES_NO_HABIT)
|
val intent = IntentFactory().startEditActivity(activity!!, HabitType.YES_NO.value)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.buttonMeasurable.setOnClickListener {
|
binding.buttonMeasurable.setOnClickListener {
|
||||||
val intent = IntentFactory().startEditActivity(activity!!, Habit.NUMBER_HABIT)
|
val intent = IntentFactory().startEditActivity(activity!!, HabitType.NUMERICAL.value)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import org.isoron.uhabits.inject.ActivityContextModule
|
|||||||
import org.isoron.uhabits.inject.DaggerHabitsActivityComponent
|
import org.isoron.uhabits.inject.DaggerHabitsActivityComponent
|
||||||
import org.isoron.uhabits.utils.restartWithFade
|
import org.isoron.uhabits.utils.restartWithFade
|
||||||
|
|
||||||
class ListHabitsActivity : AppCompatActivity() {
|
class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
||||||
|
|
||||||
var pureBlack: Boolean = false
|
var pureBlack: Boolean = false
|
||||||
lateinit var taskRunner: TaskRunner
|
lateinit var taskRunner: TaskRunner
|
||||||
@@ -51,6 +51,11 @@ class ListHabitsActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private lateinit var menu: ListHabitsMenu
|
private lateinit var menu: ListHabitsMenu
|
||||||
|
|
||||||
|
override fun onQuestionMarksChanged() {
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
menu.behavior.onPreferencesChanged()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@@ -63,6 +68,7 @@ class ListHabitsActivity : AppCompatActivity() {
|
|||||||
component.themeSwitcher.apply()
|
component.themeSwitcher.apply()
|
||||||
|
|
||||||
prefs = appComponent.preferences
|
prefs = appComponent.preferences
|
||||||
|
prefs.addListener(this)
|
||||||
pureBlack = prefs.isPureBlackEnabled
|
pureBlack = prefs.isPureBlackEnabled
|
||||||
midnightTimer = appComponent.midnightTimer
|
midnightTimer = appComponent.midnightTimer
|
||||||
rootView = component.listHabitsRootView
|
rootView = component.listHabitsRootView
|
||||||
@@ -77,7 +83,7 @@ class ListHabitsActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
midnightTimer.onPause()
|
midnightTimer.onPause()
|
||||||
screen.onDettached()
|
screen.onDetached()
|
||||||
adapter.cancelRefresh()
|
adapter.cancelRefresh()
|
||||||
super.onPause()
|
super.onPause()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class ListHabitsMenu @Inject constructor(
|
|||||||
@ActivityContext context: Context,
|
@ActivityContext context: Context,
|
||||||
private val preferences: Preferences,
|
private val preferences: Preferences,
|
||||||
private val themeSwitcher: ThemeSwitcher,
|
private val themeSwitcher: ThemeSwitcher,
|
||||||
private val behavior: ListHabitsMenuBehavior
|
val behavior: ListHabitsMenuBehavior
|
||||||
) {
|
) {
|
||||||
val activity = (context as AppCompatActivity)
|
val activity = (context as AppCompatActivity)
|
||||||
|
|
||||||
@@ -52,6 +52,11 @@ class ListHabitsMenu @Inject constructor(
|
|||||||
nightModeItem.isChecked = themeSwitcher.isNightMode
|
nightModeItem.isChecked = themeSwitcher.isNightMode
|
||||||
hideArchivedItem.isChecked = !preferences.showArchived
|
hideArchivedItem.isChecked = !preferences.showArchived
|
||||||
hideCompletedItem.isChecked = !preferences.showCompleted
|
hideCompletedItem.isChecked = !preferences.showCompleted
|
||||||
|
if (preferences.areQuestionMarksEnabled || preferences.isSkipEnabled) {
|
||||||
|
hideCompletedItem.title = activity.resources.getString(R.string.hide_entered)
|
||||||
|
} else {
|
||||||
|
hideCompletedItem.title = activity.resources.getString(R.string.hide_completed)
|
||||||
|
}
|
||||||
updateArrows(menu)
|
updateArrows(menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import org.isoron.uhabits.utils.addAtBottom
|
|||||||
import org.isoron.uhabits.utils.addAtTop
|
import org.isoron.uhabits.utils.addAtTop
|
||||||
import org.isoron.uhabits.utils.addBelow
|
import org.isoron.uhabits.utils.addBelow
|
||||||
import org.isoron.uhabits.utils.buildToolbar
|
import org.isoron.uhabits.utils.buildToolbar
|
||||||
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
import org.isoron.uhabits.utils.dim
|
import org.isoron.uhabits.utils.dim
|
||||||
import org.isoron.uhabits.utils.dp
|
import org.isoron.uhabits.utils.dp
|
||||||
import org.isoron.uhabits.utils.setupToolbar
|
import org.isoron.uhabits.utils.setupToolbar
|
||||||
@@ -93,6 +94,7 @@ class ListHabitsRootView @Inject constructor(
|
|||||||
title = resources.getString(R.string.main_activity_title),
|
title = resources.getString(R.string.main_activity_title),
|
||||||
color = PaletteColor(17),
|
color = PaletteColor(17),
|
||||||
displayHomeAsUpEnabled = false,
|
displayHomeAsUpEnabled = false,
|
||||||
|
theme = currentTheme(),
|
||||||
)
|
)
|
||||||
addView(rootView, MATCH_PARENT, MATCH_PARENT)
|
addView(rootView, MATCH_PARENT, MATCH_PARENT)
|
||||||
listAdapter.setListView(listView)
|
listAdapter.setListView(listView)
|
||||||
|
|||||||
@@ -24,10 +24,12 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import dagger.Lazy
|
import dagger.Lazy
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.activities.common.dialogs.CheckmarkPopup
|
||||||
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory
|
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory
|
||||||
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
|
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
|
||||||
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
import org.isoron.uhabits.activities.common.dialogs.NumberPopup
|
||||||
import org.isoron.uhabits.activities.habits.edit.HabitTypeDialog
|
import org.isoron.uhabits.activities.habits.edit.HabitTypeDialog
|
||||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
|
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
|
||||||
import org.isoron.uhabits.core.commands.ArchiveHabitsCommand
|
import org.isoron.uhabits.core.commands.ArchiveHabitsCommand
|
||||||
@@ -40,6 +42,7 @@ import org.isoron.uhabits.core.commands.EditHabitCommand
|
|||||||
import org.isoron.uhabits.core.commands.UnarchiveHabitsCommand
|
import org.isoron.uhabits.core.commands.UnarchiveHabitsCommand
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||||
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
||||||
import org.isoron.uhabits.core.ui.callbacks.OnColorPickedCallback
|
import org.isoron.uhabits.core.ui.callbacks.OnColorPickedCallback
|
||||||
@@ -60,6 +63,7 @@ import org.isoron.uhabits.tasks.ExportDBTaskFactory
|
|||||||
import org.isoron.uhabits.tasks.ImportDataTask
|
import org.isoron.uhabits.tasks.ImportDataTask
|
||||||
import org.isoron.uhabits.tasks.ImportDataTaskFactory
|
import org.isoron.uhabits.tasks.ImportDataTaskFactory
|
||||||
import org.isoron.uhabits.utils.copyTo
|
import org.isoron.uhabits.utils.copyTo
|
||||||
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
import org.isoron.uhabits.utils.restartWithFade
|
import org.isoron.uhabits.utils.restartWithFade
|
||||||
import org.isoron.uhabits.utils.showMessage
|
import org.isoron.uhabits.utils.showMessage
|
||||||
import org.isoron.uhabits.utils.showSendEmailScreen
|
import org.isoron.uhabits.utils.showSendEmailScreen
|
||||||
@@ -88,8 +92,9 @@ class ListHabitsScreen
|
|||||||
private val exportDBFactory: ExportDBTaskFactory,
|
private val exportDBFactory: ExportDBTaskFactory,
|
||||||
private val importTaskFactory: ImportDataTaskFactory,
|
private val importTaskFactory: ImportDataTaskFactory,
|
||||||
private val colorPickerFactory: ColorPickerDialogFactory,
|
private val colorPickerFactory: ColorPickerDialogFactory,
|
||||||
private val numberPickerFactory: NumberPickerFactory,
|
private val behavior: Lazy<ListHabitsBehavior>,
|
||||||
private val behavior: Lazy<ListHabitsBehavior>
|
private val preferences: Preferences,
|
||||||
|
private val rootView: Lazy<ListHabitsRootView>,
|
||||||
) : CommandRunner.Listener,
|
) : CommandRunner.Listener,
|
||||||
ListHabitsBehavior.Screen,
|
ListHabitsBehavior.Screen,
|
||||||
ListHabitsMenuBehavior.Screen,
|
ListHabitsMenuBehavior.Screen,
|
||||||
@@ -101,7 +106,7 @@ class ListHabitsScreen
|
|||||||
commandRunner.addListener(this)
|
commandRunner.addListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onDettached() {
|
fun onDetached() {
|
||||||
commandRunner.removeListener(this)
|
commandRunner.removeListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,17 +222,47 @@ class ListHabitsScreen
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showColorPicker(defaultColor: PaletteColor, callback: OnColorPickedCallback) {
|
override fun showColorPicker(defaultColor: PaletteColor, callback: OnColorPickedCallback) {
|
||||||
val picker = colorPickerFactory.create(defaultColor)
|
val picker = colorPickerFactory.create(defaultColor, themeSwitcher.currentTheme!!)
|
||||||
picker.setListener(callback)
|
picker.setListener(callback)
|
||||||
picker.show(activity.supportFragmentManager, "picker")
|
picker.show(activity.supportFragmentManager, "picker")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showNumberPicker(
|
override fun showNumberPopup(
|
||||||
value: Double,
|
value: Double,
|
||||||
unit: String,
|
notes: String,
|
||||||
callback: ListHabitsBehavior.NumberPickerCallback
|
callback: ListHabitsBehavior.NumberPickerCallback
|
||||||
) {
|
) {
|
||||||
numberPickerFactory.create(value, unit, callback).show()
|
val view = rootView.get()
|
||||||
|
NumberPopup(
|
||||||
|
context = context,
|
||||||
|
prefs = preferences,
|
||||||
|
anchor = view,
|
||||||
|
notes = notes,
|
||||||
|
value = value,
|
||||||
|
).apply {
|
||||||
|
onToggle = { value, notes -> callback.onNumberPicked(value, notes) }
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showCheckmarkPopup(
|
||||||
|
selectedValue: Int,
|
||||||
|
notes: String,
|
||||||
|
color: PaletteColor,
|
||||||
|
callback: ListHabitsBehavior.CheckMarkDialogCallback
|
||||||
|
) {
|
||||||
|
val view = rootView.get()
|
||||||
|
CheckmarkPopup(
|
||||||
|
context = context,
|
||||||
|
prefs = preferences,
|
||||||
|
anchor = view,
|
||||||
|
color = view.currentTheme().color(color).toInt(),
|
||||||
|
notes = notes,
|
||||||
|
value = selectedValue,
|
||||||
|
).apply {
|
||||||
|
onToggle = { value, notes -> callback.onNotesSaved(value, notes) }
|
||||||
|
show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getExecuteString(command: Command): String? {
|
private fun getExecuteString(command: Command): String? {
|
||||||
|
|||||||
@@ -37,13 +37,15 @@ import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO
|
|||||||
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
import org.isoron.uhabits.utils.dim
|
import org.isoron.uhabits.utils.drawNotesIndicator
|
||||||
import org.isoron.uhabits.utils.getFontAwesome
|
import org.isoron.uhabits.utils.getFontAwesome
|
||||||
import org.isoron.uhabits.utils.showMessage
|
import org.isoron.uhabits.utils.sp
|
||||||
import org.isoron.uhabits.utils.sres
|
import org.isoron.uhabits.utils.sres
|
||||||
import org.isoron.uhabits.utils.toMeasureSpec
|
import org.isoron.uhabits.utils.toMeasureSpec
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
const val TOGGLE_DELAY_MILLIS = 2000L
|
||||||
|
|
||||||
class CheckmarkButtonViewFactory
|
class CheckmarkButtonViewFactory
|
||||||
@Inject constructor(
|
@Inject constructor(
|
||||||
@ActivityContext val context: Context,
|
@ActivityContext val context: Context,
|
||||||
@@ -71,33 +73,42 @@ class CheckmarkButtonView(
|
|||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
var onToggle: (Int) -> Unit = {}
|
var notes = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
var onToggle: (Int, String, Long) -> Unit = { _, _, _ -> }
|
||||||
|
|
||||||
|
var onEdit: () -> Unit = { }
|
||||||
|
|
||||||
private var drawer = Drawer()
|
private var drawer = Drawer()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
isFocusable = false
|
|
||||||
setOnClickListener(this)
|
setOnClickListener(this)
|
||||||
setOnLongClickListener(this)
|
setOnLongClickListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun performToggle() {
|
fun performToggle(delay: Long) {
|
||||||
value = if (preferences.isSkipEnabled) {
|
value = Entry.nextToggleValue(
|
||||||
Entry.nextToggleValueWithSkip(value)
|
value = value,
|
||||||
} else {
|
isSkipEnabled = preferences.isSkipEnabled,
|
||||||
Entry.nextToggleValueWithoutSkip(value)
|
areQuestionMarksEnabled = preferences.areQuestionMarksEnabled
|
||||||
}
|
)
|
||||||
onToggle(value)
|
onToggle(value, notes, delay)
|
||||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
if (preferences.isShortToggleEnabled) performToggle()
|
if (preferences.isShortToggleEnabled) performToggle(TOGGLE_DELAY_MILLIS)
|
||||||
else showMessage(resources.getString(R.string.long_press_to_toggle))
|
else onEdit()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongClick(v: View): Boolean {
|
override fun onLongClick(v: View): Boolean {
|
||||||
performToggle()
|
if (preferences.isShortToggleEnabled) onEdit()
|
||||||
|
else performToggle(TOGGLE_DELAY_MILLIS)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +142,7 @@ class CheckmarkButtonView(
|
|||||||
paint.color = when (value) {
|
paint.color = when (value) {
|
||||||
YES_MANUAL, YES_AUTO, SKIP -> color
|
YES_MANUAL, YES_AUTO, SKIP -> color
|
||||||
NO -> {
|
NO -> {
|
||||||
if (preferences.areQuestionMarksEnabled()) mediumContrastColor
|
if (preferences.areQuestionMarksEnabled) mediumContrastColor
|
||||||
else lowContrastColor
|
else lowContrastColor
|
||||||
}
|
}
|
||||||
else -> lowContrastColor
|
else -> lowContrastColor
|
||||||
@@ -140,11 +151,16 @@ class CheckmarkButtonView(
|
|||||||
SKIP -> R.string.fa_skipped
|
SKIP -> R.string.fa_skipped
|
||||||
NO -> R.string.fa_times
|
NO -> R.string.fa_times
|
||||||
UNKNOWN -> {
|
UNKNOWN -> {
|
||||||
if (preferences.areQuestionMarksEnabled()) R.string.fa_question
|
if (preferences.areQuestionMarksEnabled) R.string.fa_question
|
||||||
else R.string.fa_times
|
else R.string.fa_times
|
||||||
}
|
}
|
||||||
else -> R.string.fa_check
|
else -> R.string.fa_check
|
||||||
}
|
}
|
||||||
|
paint.textSize = when {
|
||||||
|
id == R.string.fa_question -> sp(12.0f)
|
||||||
|
value == YES_AUTO -> sp(13.0f)
|
||||||
|
else -> sp(14.0f)
|
||||||
|
}
|
||||||
if (value == YES_AUTO) {
|
if (value == YES_AUTO) {
|
||||||
paint.strokeWidth = 5f
|
paint.strokeWidth = 5f
|
||||||
paint.style = Paint.Style.STROKE
|
paint.style = Paint.Style.STROKE
|
||||||
@@ -153,11 +169,6 @@ class CheckmarkButtonView(
|
|||||||
paint.style = Paint.Style.FILL
|
paint.style = Paint.Style.FILL
|
||||||
}
|
}
|
||||||
|
|
||||||
paint.textSize = when (id) {
|
|
||||||
UNKNOWN -> dim(R.dimen.smallerTextSize)
|
|
||||||
else -> dim(R.dimen.smallTextSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
val label = resources.getString(id)
|
val label = resources.getString(id)
|
||||||
val em = paint.measureText("m")
|
val em = paint.measureText("m")
|
||||||
|
|
||||||
@@ -170,6 +181,8 @@ class CheckmarkButtonView(
|
|||||||
paint.style = Paint.Style.FILL
|
paint.style = Paint.Style.FILL
|
||||||
canvas.drawText(label, rect.centerX(), rect.centerY(), paint)
|
canvas.drawText(label, rect.centerX(), rect.centerY(), paint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawNotesIndicator(canvas, color, em, notes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,19 @@ class CheckmarkPanelView(
|
|||||||
setupButtons()
|
setupButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
var onToggle: (Timestamp, Int) -> Unit = { _, _ -> }
|
var notes = arrayOf<String>()
|
||||||
|
set(values) {
|
||||||
|
field = values
|
||||||
|
setupButtons()
|
||||||
|
}
|
||||||
|
|
||||||
|
var onToggle: (Timestamp, Int, String, Long) -> Unit = { _, _, _, _ -> }
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
setupButtons()
|
||||||
|
}
|
||||||
|
|
||||||
|
var onEdit: (Timestamp) -> Unit = { _ -> }
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
setupButtons()
|
setupButtons()
|
||||||
@@ -72,8 +84,13 @@ class CheckmarkPanelView(
|
|||||||
index + dataOffset < values.size -> values[index + dataOffset]
|
index + dataOffset < values.size -> values[index + dataOffset]
|
||||||
else -> UNKNOWN
|
else -> UNKNOWN
|
||||||
}
|
}
|
||||||
|
button.notes = when {
|
||||||
|
index + dataOffset < notes.size -> notes[index + dataOffset]
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
button.color = color
|
button.color = color
|
||||||
button.onToggle = { value -> onToggle(timestamp, value) }
|
button.onToggle = { value, notes, delay -> onToggle(timestamp, value, notes, delay) }
|
||||||
|
button.onEdit = { onEdit(timestamp) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,8 +124,9 @@ class HabitCardListAdapter @Inject constructor(
|
|||||||
val habit = cache.getHabitByPosition(position)
|
val habit = cache.getHabitByPosition(position)
|
||||||
val score = cache.getScore(habit!!.id!!)
|
val score = cache.getScore(habit!!.id!!)
|
||||||
val checkmarks = cache.getCheckmarks(habit.id!!)
|
val checkmarks = cache.getCheckmarks(habit.id!!)
|
||||||
|
val notes = cache.getNotes(habit.id!!)
|
||||||
val selected = selected.contains(habit)
|
val selected = selected.contains(habit)
|
||||||
listView!!.bindCardView(holder, habit, score, checkmarks, selected)
|
listView!!.bindCardView(holder, habit, score, checkmarks, notes, selected)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewAttachedToWindow(holder: HabitCardViewHolder) {
|
override fun onViewAttachedToWindow(holder: HabitCardViewHolder) {
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ class HabitCardListView(
|
|||||||
habit: Habit,
|
habit: Habit,
|
||||||
score: Double,
|
score: Double,
|
||||||
checkmarks: IntArray,
|
checkmarks: IntArray,
|
||||||
|
notes: Array<String>,
|
||||||
selected: Boolean
|
selected: Boolean
|
||||||
): View {
|
): View {
|
||||||
val cardView = holder.itemView as HabitCardView
|
val cardView = holder.itemView as HabitCardView
|
||||||
@@ -98,6 +99,7 @@ class HabitCardListView(
|
|||||||
cardView.score = score
|
cardView.score = score
|
||||||
cardView.unit = habit.unit
|
cardView.unit = habit.unit
|
||||||
cardView.threshold = habit.targetValue / habit.frequency.denominator
|
cardView.threshold = habit.targetValue / habit.frequency.denominator
|
||||||
|
cardView.notes = notes
|
||||||
|
|
||||||
val detector = GestureDetector(context, CardViewGestureDetector(holder))
|
val detector = GestureDetector(context, CardViewGestureDetector(holder))
|
||||||
cardView.setOnTouchListener { _, ev ->
|
cardView.setOnTouchListener { _, ev ->
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ package org.isoron.uhabits.activities.habits.list.views
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.text.LineBreaker.BREAK_STRATEGY_BALANCED
|
import android.graphics.text.LineBreaker.BREAK_STRATEGY_BALANCED
|
||||||
|
import android.os.Build
|
||||||
import android.os.Build.VERSION.SDK_INT
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import android.os.Build.VERSION_CODES.M
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
@@ -33,6 +33,7 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
|||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.activities.common.views.RingView
|
import org.isoron.uhabits.activities.common.views.RingView
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
@@ -41,9 +42,9 @@ import org.isoron.uhabits.core.models.Timestamp
|
|||||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||||
import org.isoron.uhabits.core.utils.DateUtils
|
import org.isoron.uhabits.core.utils.DateUtils
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
import org.isoron.uhabits.utils.dp
|
import org.isoron.uhabits.utils.dp
|
||||||
import org.isoron.uhabits.utils.sres
|
import org.isoron.uhabits.utils.sres
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class HabitCardViewFactory
|
class HabitCardViewFactory
|
||||||
@@ -56,6 +57,13 @@ class HabitCardViewFactory
|
|||||||
fun create() = HabitCardView(context, checkmarkPanelFactory, numberPanelFactory, behavior)
|
fun create() = HabitCardView(context, checkmarkPanelFactory, numberPanelFactory, behavior)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class DelayedToggle(
|
||||||
|
var habit: Habit,
|
||||||
|
var timestamp: Timestamp,
|
||||||
|
var value: Int,
|
||||||
|
var notes: String
|
||||||
|
)
|
||||||
|
|
||||||
class HabitCardView(
|
class HabitCardView(
|
||||||
@ActivityContext context: Context,
|
@ActivityContext context: Context,
|
||||||
checkmarkPanelFactory: CheckmarkPanelViewFactory,
|
checkmarkPanelFactory: CheckmarkPanelViewFactory,
|
||||||
@@ -114,12 +122,22 @@ class HabitCardView(
|
|||||||
numberPanel.threshold = value
|
numberPanel.threshold = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var notes
|
||||||
|
get() = checkmarkPanel.notes
|
||||||
|
set(values) {
|
||||||
|
checkmarkPanel.notes = values
|
||||||
|
numberPanel.notes = values
|
||||||
|
}
|
||||||
|
|
||||||
var checkmarkPanel: CheckmarkPanelView
|
var checkmarkPanel: CheckmarkPanelView
|
||||||
private var numberPanel: NumberPanelView
|
private var numberPanel: NumberPanelView
|
||||||
private var innerFrame: LinearLayout
|
private var innerFrame: LinearLayout
|
||||||
private var label: TextView
|
private var label: TextView
|
||||||
private var scoreRing: RingView
|
private var scoreRing: RingView
|
||||||
|
|
||||||
|
private var currentToggleTaskId = 0
|
||||||
|
private var queuedToggles = mutableListOf<DelayedToggle>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
scoreRing = RingView(context).apply {
|
scoreRing = RingView(context).apply {
|
||||||
val thickness = dp(3f)
|
val thickness = dp(3f)
|
||||||
@@ -136,13 +154,22 @@ class HabitCardView(
|
|||||||
maxLines = 2
|
maxLines = 2
|
||||||
ellipsize = TextUtils.TruncateAt.END
|
ellipsize = TextUtils.TruncateAt.END
|
||||||
layoutParams = LinearLayout.LayoutParams(0, WRAP_CONTENT, 1f)
|
layoutParams = LinearLayout.LayoutParams(0, WRAP_CONTENT, 1f)
|
||||||
if (SDK_INT >= M) breakStrategy = BREAK_STRATEGY_BALANCED
|
if (SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
breakStrategy = BREAK_STRATEGY_BALANCED
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkmarkPanel = checkmarkPanelFactory.create().apply {
|
checkmarkPanel = checkmarkPanelFactory.create().apply {
|
||||||
onToggle = { timestamp, value ->
|
onToggle = { timestamp, value, notes, delay ->
|
||||||
|
if (delay > 0) triggerRipple(timestamp)
|
||||||
|
habit?.let {
|
||||||
|
val taskId = queueToggle(it, timestamp, value, notes);
|
||||||
|
{ runPendingToggles(taskId) }.delay(delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onEdit = { timestamp ->
|
||||||
triggerRipple(timestamp)
|
triggerRipple(timestamp)
|
||||||
habit?.let { behavior.onToggle(it, timestamp, value) }
|
habit?.let { behavior.onEdit(it, timestamp) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +205,25 @@ class HabitCardView(
|
|||||||
addView(innerFrame)
|
addView(innerFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun runPendingToggles(id: Int) {
|
||||||
|
if (currentToggleTaskId != id) return
|
||||||
|
for ((h, t, v, n) in queuedToggles) behavior.onToggle(h, t, v, n)
|
||||||
|
queuedToggles.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun queueToggle(
|
||||||
|
it: Habit,
|
||||||
|
timestamp: Timestamp,
|
||||||
|
value: Int,
|
||||||
|
notes: String,
|
||||||
|
): Int {
|
||||||
|
currentToggleTaskId += 1
|
||||||
|
queuedToggles.add(DelayedToggle(it, timestamp, value, notes))
|
||||||
|
return currentToggleTaskId
|
||||||
|
}
|
||||||
|
|
||||||
override fun onModelChange() {
|
override fun onModelChange() {
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
habit?.let { copyAttributesFrom(it) }
|
habit?.let { copyAttributesFrom(it) }
|
||||||
@@ -213,7 +259,7 @@ class HabitCardView(
|
|||||||
fun getActiveColor(habit: Habit): Int {
|
fun getActiveColor(habit: Habit): Int {
|
||||||
return when (habit.isArchived) {
|
return when (habit.isArchived) {
|
||||||
true -> sres.getColor(R.attr.contrast60)
|
true -> sres.getColor(R.attr.contrast60)
|
||||||
false -> habit.color.toThemedAndroidColor(context)
|
false -> currentTheme().color(habit.color).toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,6 +281,7 @@ class HabitCardView(
|
|||||||
numberPanel.apply {
|
numberPanel.apply {
|
||||||
color = c
|
color = c
|
||||||
units = h.unit
|
units = h.unit
|
||||||
|
targetType = h.targetType
|
||||||
threshold = h.targetValue
|
threshold = h.targetValue
|
||||||
visibility = when (h.isNumerical) {
|
visibility = when (h.isNumerical) {
|
||||||
true -> View.VISIBLE
|
true -> View.VISIBLE
|
||||||
@@ -261,4 +308,10 @@ class HabitCardView(
|
|||||||
}
|
}
|
||||||
innerFrame.setBackgroundResource(background)
|
innerFrame.setBackgroundResource(background)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun (() -> Unit).delay(delayInMillis: Long) {
|
||||||
|
Handler(Looper.getMainLooper()).postDelayed(this, delayInMillis)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,13 +29,16 @@ import android.view.View
|
|||||||
import android.view.View.OnClickListener
|
import android.view.View.OnClickListener
|
||||||
import android.view.View.OnLongClickListener
|
import android.view.View.OnLongClickListener
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.core.models.Entry
|
||||||
|
import org.isoron.uhabits.core.models.NumericalHabitType.AT_LEAST
|
||||||
|
import org.isoron.uhabits.core.models.NumericalHabitType.AT_MOST
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils.getDimension
|
import org.isoron.uhabits.utils.InterfaceUtils.getDimension
|
||||||
import org.isoron.uhabits.utils.StyledResources
|
|
||||||
import org.isoron.uhabits.utils.dim
|
import org.isoron.uhabits.utils.dim
|
||||||
|
import org.isoron.uhabits.utils.drawNotesIndicator
|
||||||
import org.isoron.uhabits.utils.getFontAwesome
|
import org.isoron.uhabits.utils.getFontAwesome
|
||||||
import org.isoron.uhabits.utils.showMessage
|
import org.isoron.uhabits.utils.sres
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -88,13 +91,25 @@ class NumberButtonView(
|
|||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
var units = ""
|
var targetType = AT_LEAST
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
var onEdit: () -> Unit = {}
|
var units = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
var notes = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
var onEdit: () -> Unit = { }
|
||||||
|
|
||||||
private var drawer: Drawer = Drawer(context)
|
private var drawer: Drawer = Drawer(context)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -103,8 +118,7 @@ class NumberButtonView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
if (preferences.isShortToggleEnabled) onEdit()
|
onEdit()
|
||||||
else showMessage(resources.getString(R.string.long_press_to_edit))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongClick(v: View): Boolean {
|
override fun onLongClick(v: View): Boolean {
|
||||||
@@ -127,11 +141,16 @@ class NumberButtonView(
|
|||||||
|
|
||||||
private val em: Float
|
private val em: Float
|
||||||
private val rect: RectF = RectF()
|
private val rect: RectF = RectF()
|
||||||
private val sr = StyledResources(context)
|
|
||||||
|
|
||||||
private val lowContrast: Int
|
private val lowContrast: Int
|
||||||
private val mediumContrast: Int
|
private val mediumContrast: Int
|
||||||
|
|
||||||
|
private val paint = TextPaint().apply {
|
||||||
|
typeface = getFontAwesome()
|
||||||
|
isAntiAlias = true
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
}
|
||||||
|
|
||||||
private val pUnit: TextPaint = TextPaint().apply {
|
private val pUnit: TextPaint = TextPaint().apply {
|
||||||
textSize = getDimension(context, R.dimen.smallerTextSize)
|
textSize = getDimension(context, R.dimen.smallerTextSize)
|
||||||
typeface = NORMAL_TYPEFACE
|
typeface = NORMAL_TYPEFACE
|
||||||
@@ -148,15 +167,16 @@ class NumberButtonView(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
em = pNumber.measureText("m")
|
em = pNumber.measureText("m")
|
||||||
lowContrast = sr.getColor(R.attr.contrast40)
|
lowContrast = sres.getColor(R.attr.contrast40)
|
||||||
mediumContrast = sr.getColor(R.attr.contrast60)
|
mediumContrast = sres.getColor(R.attr.contrast60)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun draw(canvas: Canvas) {
|
fun draw(canvas: Canvas) {
|
||||||
val activeColor = when {
|
val activeColor = when {
|
||||||
value <= 0.0 -> lowContrast
|
value < 0.0 -> lowContrast
|
||||||
value < threshold -> mediumContrast
|
(targetType == AT_LEAST) && (value >= threshold) -> color
|
||||||
else -> color
|
(targetType == AT_MOST) && (value <= threshold) -> color
|
||||||
|
else -> mediumContrast
|
||||||
}
|
}
|
||||||
|
|
||||||
val label: String
|
val label: String
|
||||||
@@ -164,12 +184,17 @@ class NumberButtonView(
|
|||||||
val textSize: Float
|
val textSize: Float
|
||||||
|
|
||||||
when {
|
when {
|
||||||
|
value == Entry.SKIP.toDouble() / 1000 -> {
|
||||||
|
label = resources.getString(R.string.fa_skipped)
|
||||||
|
textSize = dim(R.dimen.smallTextSize)
|
||||||
|
typeface = getFontAwesome()
|
||||||
|
}
|
||||||
value >= 0 -> {
|
value >= 0 -> {
|
||||||
label = value.toShortString()
|
label = value.toShortString()
|
||||||
typeface = BOLD_TYPEFACE
|
typeface = BOLD_TYPEFACE
|
||||||
textSize = dim(R.dimen.smallTextSize)
|
textSize = dim(R.dimen.smallTextSize)
|
||||||
}
|
}
|
||||||
preferences.areQuestionMarksEnabled() -> {
|
preferences.areQuestionMarksEnabled -> {
|
||||||
label = resources.getString(R.string.fa_question)
|
label = resources.getString(R.string.fa_question)
|
||||||
typeface = getFontAwesome()
|
typeface = getFontAwesome()
|
||||||
textSize = dim(R.dimen.smallerTextSize)
|
textSize = dim(R.dimen.smallerTextSize)
|
||||||
@@ -196,6 +221,8 @@ class NumberButtonView(
|
|||||||
rect.offset(0f, 1.3f * em)
|
rect.offset(0f, 1.3f * em)
|
||||||
canvas.drawText(units, rect.centerX(), rect.centerY(), pUnit)
|
canvas.drawText(units, rect.centerX(), rect.centerY(), pUnit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawNotesIndicator(canvas, color, em, notes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
package org.isoron.uhabits.activities.habits.list.views
|
package org.isoron.uhabits.activities.habits.list.views
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import org.isoron.uhabits.core.models.NumericalHabitType
|
||||||
import org.isoron.uhabits.core.models.Timestamp
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.utils.DateUtils
|
import org.isoron.uhabits.core.utils.DateUtils
|
||||||
@@ -47,6 +48,12 @@ class NumberPanelView(
|
|||||||
setupButtons()
|
setupButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var targetType = NumericalHabitType.AT_LEAST
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
setupButtons()
|
||||||
|
}
|
||||||
|
|
||||||
var threshold = 0.0
|
var threshold = 0.0
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
@@ -65,7 +72,13 @@ class NumberPanelView(
|
|||||||
setupButtons()
|
setupButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
var onEdit: (Timestamp) -> Unit = {}
|
var notes = arrayOf<String>()
|
||||||
|
set(values) {
|
||||||
|
field = values
|
||||||
|
setupButtons()
|
||||||
|
}
|
||||||
|
|
||||||
|
var onEdit: (Timestamp) -> Unit = { _ -> }
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
setupButtons()
|
setupButtons()
|
||||||
@@ -83,7 +96,12 @@ class NumberPanelView(
|
|||||||
index + dataOffset < values.size -> values[index + dataOffset]
|
index + dataOffset < values.size -> values[index + dataOffset]
|
||||||
else -> 0.0
|
else -> 0.0
|
||||||
}
|
}
|
||||||
|
button.notes = when {
|
||||||
|
index + dataOffset < notes.size -> notes[index + dataOffset]
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
button.color = color
|
button.color = color
|
||||||
|
button.targetType = targetType
|
||||||
button.threshold = threshold
|
button.threshold = threshold
|
||||||
button.units = units
|
button.units = units
|
||||||
button.onEdit = { onEdit(timestamp) }
|
button.onEdit = { onEdit(timestamp) }
|
||||||
|
|||||||
@@ -23,21 +23,25 @@ import android.os.Bundle
|
|||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.AndroidDirFinder
|
import org.isoron.uhabits.AndroidDirFinder
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
import org.isoron.uhabits.activities.HabitsDirFinder
|
import org.isoron.uhabits.activities.HabitsDirFinder
|
||||||
|
import org.isoron.uhabits.activities.common.dialogs.CheckmarkPopup
|
||||||
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
|
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
|
||||||
import org.isoron.uhabits.activities.common.dialogs.HistoryEditorDialog
|
import org.isoron.uhabits.activities.common.dialogs.HistoryEditorDialog
|
||||||
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
import org.isoron.uhabits.activities.common.dialogs.NumberPopup
|
||||||
import org.isoron.uhabits.core.commands.Command
|
import org.isoron.uhabits.core.commands.Command
|
||||||
import org.isoron.uhabits.core.commands.CommandRunner
|
import org.isoron.uhabits.core.commands.CommandRunner
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
|
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||||
@@ -45,6 +49,7 @@ import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitMenuPresenter
|
|||||||
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitPresenter
|
||||||
import org.isoron.uhabits.core.ui.views.OnDateClickedListener
|
import org.isoron.uhabits.core.ui.views.OnDateClickedListener
|
||||||
import org.isoron.uhabits.intents.IntentFactory
|
import org.isoron.uhabits.intents.IntentFactory
|
||||||
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
import org.isoron.uhabits.utils.showMessage
|
import org.isoron.uhabits.utils.showMessage
|
||||||
import org.isoron.uhabits.utils.showSendFileScreen
|
import org.isoron.uhabits.utils.showSendFileScreen
|
||||||
import org.isoron.uhabits.widgets.WidgetUpdater
|
import org.isoron.uhabits.widgets.WidgetUpdater
|
||||||
@@ -161,12 +166,49 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
|
|||||||
window.decorView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
|
window.decorView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showNumberPicker(
|
override fun showNumberPopup(
|
||||||
value: Double,
|
value: Double,
|
||||||
unit: String,
|
notes: String,
|
||||||
callback: ListHabitsBehavior.NumberPickerCallback,
|
preferences: Preferences,
|
||||||
|
callback: ListHabitsBehavior.NumberPickerCallback
|
||||||
) {
|
) {
|
||||||
NumberPickerFactory(this@ShowHabitActivity).create(value, unit, callback).show()
|
val anchor = getPopupAnchor() ?: return
|
||||||
|
NumberPopup(
|
||||||
|
context = this@ShowHabitActivity,
|
||||||
|
prefs = preferences,
|
||||||
|
notes = notes,
|
||||||
|
anchor = anchor,
|
||||||
|
value = value,
|
||||||
|
).apply {
|
||||||
|
onToggle = { v, n -> callback.onNumberPicked(v, n) }
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showCheckmarkPopup(
|
||||||
|
selectedValue: Int,
|
||||||
|
notes: String,
|
||||||
|
preferences: Preferences,
|
||||||
|
color: PaletteColor,
|
||||||
|
callback: ListHabitsBehavior.CheckMarkDialogCallback
|
||||||
|
) {
|
||||||
|
val anchor = getPopupAnchor() ?: return
|
||||||
|
CheckmarkPopup(
|
||||||
|
context = this@ShowHabitActivity,
|
||||||
|
prefs = preferences,
|
||||||
|
notes = notes,
|
||||||
|
color = view.currentTheme().color(color).toInt(),
|
||||||
|
anchor = anchor,
|
||||||
|
value = selectedValue,
|
||||||
|
).apply {
|
||||||
|
onToggle = { v, n -> callback.onNotesSaved(v, n) }
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPopupAnchor(): View? {
|
||||||
|
val dialog = supportFragmentManager.findFragmentByTag("historyEditor") as HistoryEditorDialog?
|
||||||
|
return dialog?.dataView
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showEditHabitScreen(habit: Habit) {
|
override fun showEditHabitScreen(habit: Habit) {
|
||||||
|
|||||||
@@ -35,7 +35,12 @@ class ShowHabitView(context: Context) : FrameLayout(context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setState(data: ShowHabitState) {
|
fun setState(data: ShowHabitState) {
|
||||||
setupToolbar(binding.toolbar, title = data.title, color = data.color)
|
setupToolbar(
|
||||||
|
binding.toolbar,
|
||||||
|
title = data.title,
|
||||||
|
color = data.color,
|
||||||
|
theme = data.theme,
|
||||||
|
)
|
||||||
binding.subtitleCard.setState(data.subtitle)
|
binding.subtitleCard.setState(data.subtitle)
|
||||||
binding.overviewCard.setState(data.overview)
|
binding.overviewCard.setState(data.overview)
|
||||||
binding.notesCard.setState(data.notes)
|
binding.notesCard.setState(data.notes)
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.AdapterView
|
import android.widget.AdapterView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.platform.time.JavaLocalDateFormatter
|
import org.isoron.platform.time.JavaLocalDateFormatter
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.BarCardPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.BarCardPresenter
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.BarCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.BarCardState
|
||||||
import org.isoron.uhabits.core.ui.views.BarChart
|
import org.isoron.uhabits.core.ui.views.BarChart
|
||||||
import org.isoron.uhabits.databinding.ShowHabitBarBinding
|
import org.isoron.uhabits.databinding.ShowHabitBarBinding
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class BarCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class BarCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
@@ -37,7 +37,7 @@ class BarCardView(context: Context, attrs: AttributeSet) : LinearLayout(context,
|
|||||||
private var binding = ShowHabitBarBinding.inflate(LayoutInflater.from(context), this)
|
private var binding = ShowHabitBarBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
|
||||||
fun setState(state: BarCardState) {
|
fun setState(state: BarCardState) {
|
||||||
val androidColor = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
binding.chart.view = BarChart(state.theme, JavaLocalDateFormatter(Locale.US)).apply {
|
binding.chart.view = BarChart(state.theme, JavaLocalDateFormatter(Locale.US)).apply {
|
||||||
series = mutableListOf(state.entries.map { it.value / 1000.0 })
|
series = mutableListOf(state.entries.map { it.value / 1000.0 })
|
||||||
colors = mutableListOf(theme.color(state.color.paletteIndex))
|
colors = mutableListOf(theme.color(state.color.paletteIndex))
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ import android.content.Context
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitFrequencyBinding
|
import org.isoron.uhabits.databinding.ShowHabitFrequencyBinding
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
|
|
||||||
class FrequencyCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class FrequencyCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
|
|
||||||
private var binding = ShowHabitFrequencyBinding.inflate(LayoutInflater.from(context), this)
|
private var binding = ShowHabitFrequencyBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
|
||||||
fun setState(state: FrequencyCardState) {
|
fun setState(state: FrequencyCardState) {
|
||||||
val androidColor = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
binding.frequencyChart.setFrequency(state.frequency)
|
binding.frequencyChart.setFrequency(state.frequency)
|
||||||
binding.frequencyChart.setFirstWeekday(state.firstWeekday)
|
binding.frequencyChart.setFirstWeekday(state.firstWeekday)
|
||||||
binding.title.setTextColor(androidColor)
|
binding.title.setTextColor(androidColor)
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ import android.content.Context
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.platform.time.JavaLocalDateFormatter
|
import org.isoron.platform.time.JavaLocalDateFormatter
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardPresenter
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardState
|
||||||
import org.isoron.uhabits.core.ui.views.HistoryChart
|
import org.isoron.uhabits.core.ui.views.HistoryChart
|
||||||
import org.isoron.uhabits.databinding.ShowHabitHistoryBinding
|
import org.isoron.uhabits.databinding.ShowHabitHistoryBinding
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class HistoryCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class HistoryCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
@@ -35,7 +35,7 @@ class HistoryCardView(context: Context, attrs: AttributeSet) : LinearLayout(cont
|
|||||||
private var binding = ShowHabitHistoryBinding.inflate(LayoutInflater.from(context), this)
|
private var binding = ShowHabitHistoryBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
|
||||||
fun setState(state: HistoryCardState) {
|
fun setState(state: HistoryCardState) {
|
||||||
val androidColor = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
binding.title.setTextColor(androidColor)
|
binding.title.setTextColor(androidColor)
|
||||||
binding.chart.view = HistoryChart(
|
binding.chart.view = HistoryChart(
|
||||||
today = state.today,
|
today = state.today,
|
||||||
@@ -43,6 +43,8 @@ class HistoryCardView(context: Context, attrs: AttributeSet) : LinearLayout(cont
|
|||||||
theme = state.theme,
|
theme = state.theme,
|
||||||
dateFormatter = JavaLocalDateFormatter(Locale.getDefault()),
|
dateFormatter = JavaLocalDateFormatter(Locale.getDefault()),
|
||||||
series = state.series,
|
series = state.series,
|
||||||
|
defaultSquare = state.defaultSquare,
|
||||||
|
notesIndicators = state.notesIndicators,
|
||||||
firstWeekday = state.firstWeekday,
|
firstWeekday = state.firstWeekday,
|
||||||
)
|
)
|
||||||
binding.chart.postInvalidate()
|
binding.chart.postInvalidate()
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ import android.content.Context
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitOverviewBinding
|
import org.isoron.uhabits.databinding.ShowHabitOverviewBinding
|
||||||
import org.isoron.uhabits.utils.StyledResources
|
import org.isoron.uhabits.utils.StyledResources
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
class OverviewCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class OverviewCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
@@ -42,7 +42,7 @@ class OverviewCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setState(state: OverviewCardState) {
|
fun setState(state: OverviewCardState) {
|
||||||
val androidColor = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
val res = StyledResources(context)
|
val res = StyledResources(context)
|
||||||
val inactiveColor = res.getColor(R.attr.contrast60)
|
val inactiveColor = res.getColor(R.attr.contrast60)
|
||||||
binding.monthDiffLabel.setTextColor(if (state.scoreMonthDiff >= 0) androidColor else inactiveColor)
|
binding.monthDiffLabel.setTextColor(if (state.scoreMonthDiff >= 0) androidColor else inactiveColor)
|
||||||
|
|||||||
@@ -24,16 +24,16 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.AdapterView
|
import android.widget.AdapterView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitScoreBinding
|
import org.isoron.uhabits.databinding.ShowHabitScoreBinding
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
|
|
||||||
class ScoreCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class ScoreCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
private var binding = ShowHabitScoreBinding.inflate(LayoutInflater.from(context), this)
|
private var binding = ShowHabitScoreBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
|
||||||
fun setState(state: ScoreCardState) {
|
fun setState(state: ScoreCardState) {
|
||||||
val androidColor = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
binding.title.setTextColor(androidColor)
|
binding.title.setTextColor(androidColor)
|
||||||
binding.spinner.setSelection(state.spinnerPosition)
|
binding.spinner.setSelection(state.spinnerPosition)
|
||||||
binding.scoreView.setScores(state.scores)
|
binding.scoreView.setScores(state.scores)
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ import android.content.Context
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitStreakBinding
|
import org.isoron.uhabits.databinding.ShowHabitStreakBinding
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
|
|
||||||
class StreakCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class StreakCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
private val binding = ShowHabitStreakBinding.inflate(LayoutInflater.from(context), this)
|
private val binding = ShowHabitStreakBinding.inflate(LayoutInflater.from(context), this)
|
||||||
fun setState(state: StreakCardState) {
|
fun setState(state: StreakCardState) {
|
||||||
val color = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
binding.title.setTextColor(color)
|
binding.title.setTextColor(androidColor)
|
||||||
binding.streakChart.setColor(color)
|
binding.streakChart.setColor(androidColor)
|
||||||
binding.streakChart.setStreaks(state.bestStreaks)
|
binding.streakChart.setStreaks(state.bestStreaks)
|
||||||
postInvalidate()
|
postInvalidate()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,19 +20,19 @@ package org.isoron.uhabits.activities.habits.show.views
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.activities.habits.edit.formatFrequency
|
||||||
import org.isoron.uhabits.activities.habits.list.views.toShortString
|
import org.isoron.uhabits.activities.habits.list.views.toShortString
|
||||||
import org.isoron.uhabits.core.models.Frequency
|
import org.isoron.uhabits.core.models.NumericalHabitType
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding
|
import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils
|
import org.isoron.uhabits.utils.InterfaceUtils
|
||||||
import org.isoron.uhabits.utils.formatTime
|
import org.isoron.uhabits.utils.formatTime
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
|
|
||||||
class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
|
|
||||||
@@ -47,9 +47,13 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
|
|||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun setState(state: SubtitleCardState) {
|
fun setState(state: SubtitleCardState) {
|
||||||
val color = state.color.toThemedAndroidColor(context)
|
val color = state.theme.color(state.color).toInt()
|
||||||
val reminder = state.reminder
|
val reminder = state.reminder
|
||||||
binding.frequencyLabel.text = state.frequency.format(resources)
|
binding.frequencyLabel.text = formatFrequency(
|
||||||
|
state.frequency.numerator,
|
||||||
|
state.frequency.denominator,
|
||||||
|
resources,
|
||||||
|
)
|
||||||
binding.questionLabel.setTextColor(color)
|
binding.questionLabel.setTextColor(color)
|
||||||
binding.questionLabel.text = state.question
|
binding.questionLabel.text = state.question
|
||||||
binding.reminderLabel.text = if (reminder != null) {
|
binding.reminderLabel.text = if (reminder != null) {
|
||||||
@@ -62,7 +66,12 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
|
|||||||
binding.questionLabel.visibility = View.VISIBLE
|
binding.questionLabel.visibility = View.VISIBLE
|
||||||
binding.targetIcon.visibility = View.VISIBLE
|
binding.targetIcon.visibility = View.VISIBLE
|
||||||
binding.targetText.visibility = View.VISIBLE
|
binding.targetText.visibility = View.VISIBLE
|
||||||
if (!state.isNumerical) {
|
if (state.isNumerical) {
|
||||||
|
binding.targetIcon.text = when (state.targetType) {
|
||||||
|
NumericalHabitType.AT_LEAST -> resources.getString(R.string.fa_arrow_circle_up)
|
||||||
|
else -> resources.getString(R.string.fa_arrow_circle_down)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
binding.targetIcon.visibility = View.GONE
|
binding.targetIcon.visibility = View.GONE
|
||||||
binding.targetText.visibility = View.GONE
|
binding.targetText.visibility = View.GONE
|
||||||
}
|
}
|
||||||
@@ -72,32 +81,4 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
|
|||||||
|
|
||||||
postInvalidate()
|
postInvalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StringFormatMatches")
|
|
||||||
private fun Frequency.format(resources: Resources): String {
|
|
||||||
val num = this.numerator
|
|
||||||
val den = this.denominator
|
|
||||||
if (num == den) {
|
|
||||||
return resources.getString(R.string.every_day)
|
|
||||||
}
|
|
||||||
if (den == 7) {
|
|
||||||
return resources.getString(R.string.x_times_per_week, num)
|
|
||||||
}
|
|
||||||
if (den == 30 || den == 31) {
|
|
||||||
return resources.getString(R.string.x_times_per_month, num)
|
|
||||||
}
|
|
||||||
if (num == 1) {
|
|
||||||
if (den == 7) {
|
|
||||||
return resources.getString(R.string.every_week)
|
|
||||||
}
|
|
||||||
if (den % 7 == 0) {
|
|
||||||
return resources.getString(R.string.every_x_weeks, den / 7)
|
|
||||||
}
|
|
||||||
if (den == 30 || den == 31) {
|
|
||||||
return resources.getString(R.string.every_month)
|
|
||||||
}
|
|
||||||
return resources.getString(R.string.every_x_days, den)
|
|
||||||
}
|
|
||||||
return "$num/$den"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,15 +23,15 @@ import android.content.res.Resources
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.TargetCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.TargetCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitTargetBinding
|
import org.isoron.uhabits.databinding.ShowHabitTargetBinding
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
|
|
||||||
class TargetCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class TargetCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
private val binding = ShowHabitTargetBinding.inflate(LayoutInflater.from(context), this)
|
private val binding = ShowHabitTargetBinding.inflate(LayoutInflater.from(context), this)
|
||||||
fun setState(state: TargetCardState) {
|
fun setState(state: TargetCardState) {
|
||||||
val androidColor = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
binding.targetChart.setValues(state.values)
|
binding.targetChart.setValues(state.values)
|
||||||
binding.targetChart.setTargets(state.targets)
|
binding.targetChart.setTargets(state.targets)
|
||||||
binding.targetChart.setLabels(state.intervals.map { intervalToLabel(resources, it) })
|
binding.targetChart.setLabels(state.intervals.map { intervalToLabel(resources, it) })
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ package org.isoron.uhabits.activities.intro
|
|||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.github.paolorotolo.appintro.AppIntro2
|
import androidx.fragment.app.Fragment
|
||||||
import com.github.paolorotolo.appintro.AppIntroFragment
|
import com.github.appintro.AppIntro2
|
||||||
|
import com.github.appintro.AppIntroFragment
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +31,9 @@ import org.isoron.uhabits.R
|
|||||||
* launched for the first time.
|
* launched for the first time.
|
||||||
*/
|
*/
|
||||||
class IntroActivity : AppIntro2() {
|
class IntroActivity : AppIntro2() {
|
||||||
override fun init(savedInstanceState: Bundle?) {
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
showStatusBar(false)
|
showStatusBar(false)
|
||||||
|
|
||||||
addSlide(
|
addSlide(
|
||||||
@@ -61,11 +64,13 @@ class IntroActivity : AppIntro2() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNextPressed() {}
|
override fun onDonePressed(currentFragment: Fragment?) {
|
||||||
|
super.onDonePressed(currentFragment)
|
||||||
override fun onDonePressed() {
|
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSlideChanged() {}
|
override fun onSkipPressed(currentFragment: Fragment?) {
|
||||||
|
super.onSkipPressed(currentFragment)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,13 +32,15 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
val component = (application as HabitsApplication).component
|
val component = (application as HabitsApplication).component
|
||||||
AndroidThemeSwitcher(this, component.preferences).apply()
|
val themeSwitcher = AndroidThemeSwitcher(this, component.preferences)
|
||||||
|
themeSwitcher.apply()
|
||||||
|
|
||||||
val binding = SettingsActivityBinding.inflate(LayoutInflater.from(this))
|
val binding = SettingsActivityBinding.inflate(LayoutInflater.from(this))
|
||||||
binding.root.setupToolbar(
|
binding.root.setupToolbar(
|
||||||
toolbar = binding.toolbar,
|
toolbar = binding.toolbar,
|
||||||
title = resources.getString(R.string.settings),
|
title = resources.getString(R.string.settings),
|
||||||
color = PaletteColor(11),
|
color = PaletteColor(11),
|
||||||
|
theme = themeSwitcher.currentTheme,
|
||||||
)
|
)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import android.content.Intent
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Build.VERSION
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -90,7 +90,7 @@ class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeLis
|
|||||||
showRingtonePicker()
|
showRingtonePicker()
|
||||||
return true
|
return true
|
||||||
} else if (key == "reminderCustomize") {
|
} else if (key == "reminderCustomize") {
|
||||||
if (VERSION.SDK_INT < Build.VERSION_CODES.O) return true
|
if (SDK_INT < Build.VERSION_CODES.O) return true
|
||||||
createAndroidNotificationChannel(context!!)
|
createAndroidNotificationChannel(context!!)
|
||||||
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
||||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context!!.packageName)
|
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context!!.packageName)
|
||||||
@@ -112,7 +112,7 @@ class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeLis
|
|||||||
}
|
}
|
||||||
updateWeekdayPreference()
|
updateWeekdayPreference()
|
||||||
|
|
||||||
if (VERSION.SDK_INT < Build.VERSION_CODES.O)
|
if (SDK_INT < Build.VERSION_CODES.O)
|
||||||
findPreference("reminderCustomize").isVisible = false
|
findPreference("reminderCustomize").isVisible = false
|
||||||
else {
|
else {
|
||||||
findPreference("reminderSound").isVisible = false
|
findPreference("reminderSound").isVisible = false
|
||||||
|
|||||||
@@ -23,17 +23,17 @@ import android.os.Bundle
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
import org.isoron.uhabits.core.models.HabitMatcherBuilder
|
import org.isoron.uhabits.core.models.HabitMatcher
|
||||||
|
|
||||||
class EditSettingActivity : AppCompatActivity() {
|
class EditSettingActivity : AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
val app = applicationContext as HabitsApplication
|
val app = applicationContext as HabitsApplication
|
||||||
val habits = app.component.habitList.getFiltered(
|
val habits = app.component.habitList.getFiltered(
|
||||||
HabitMatcherBuilder()
|
HabitMatcher(
|
||||||
.setArchivedAllowed(false)
|
isArchivedAllowed = false,
|
||||||
.setCompletedAllowed(true)
|
isCompletedAllowed = true,
|
||||||
.build()
|
)
|
||||||
)
|
)
|
||||||
AndroidThemeSwitcher(this, app.component.preferences).apply()
|
AndroidThemeSwitcher(this, app.component.preferences).apply()
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import org.isoron.uhabits.core.models.Habit
|
|||||||
import org.isoron.uhabits.core.models.HabitList
|
import org.isoron.uhabits.core.models.HabitList
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.databinding.AutomationBinding
|
import org.isoron.uhabits.databinding.AutomationBinding
|
||||||
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
import org.isoron.uhabits.utils.setupToolbar
|
import org.isoron.uhabits.utils.setupToolbar
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
|
|
||||||
@@ -53,6 +54,7 @@ class EditSettingRootView(
|
|||||||
title = resources.getString(R.string.app_name),
|
title = resources.getString(R.string.app_name),
|
||||||
color = PaletteColor(11),
|
color = PaletteColor(11),
|
||||||
displayHomeAsUpEnabled = false,
|
displayHomeAsUpEnabled = false,
|
||||||
|
theme = currentTheme(),
|
||||||
)
|
)
|
||||||
populateHabitSpinner()
|
populateHabitSpinner()
|
||||||
binding.habitSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
binding.habitSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ import android.app.AlarmManager.RTC_WAKEUP
|
|||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Context.ALARM_SERVICE
|
import android.content.Context.ALARM_SERVICE
|
||||||
import android.os.Build.VERSION.SDK_INT
|
|
||||||
import android.os.Build.VERSION_CODES.M
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import org.isoron.uhabits.core.AppScope
|
import org.isoron.uhabits.core.AppScope
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
@@ -58,10 +56,7 @@ class IntentScheduler
|
|||||||
)
|
)
|
||||||
return SchedulerResult.IGNORED
|
return SchedulerResult.IGNORED
|
||||||
}
|
}
|
||||||
if (SDK_INT >= M)
|
manager.setExactAndAllowWhileIdle(alarmType, timestamp, intent)
|
||||||
manager.setExactAndAllowWhileIdle(alarmType, timestamp, intent)
|
|
||||||
else
|
|
||||||
manager.setExact(alarmType, timestamp, intent)
|
|
||||||
return SchedulerResult.OK
|
return SchedulerResult.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
package org.isoron.uhabits.intents
|
package org.isoron.uhabits.intents
|
||||||
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
|
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
import android.app.PendingIntent.getBroadcast
|
import android.app.PendingIntent.getBroadcast
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -49,7 +50,7 @@ class PendingIntentFactory
|
|||||||
action = WidgetReceiver.ACTION_ADD_REPETITION
|
action = WidgetReceiver.ACTION_ADD_REPETITION
|
||||||
if (timestamp != null) putExtra("timestamp", timestamp.unixTime)
|
if (timestamp != null) putExtra("timestamp", timestamp.unixTime)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun dismissNotification(habit: Habit): PendingIntent =
|
fun dismissNotification(habit: Habit): PendingIntent =
|
||||||
@@ -60,18 +61,19 @@ class PendingIntentFactory
|
|||||||
action = WidgetReceiver.ACTION_DISMISS_REMINDER
|
action = WidgetReceiver.ACTION_DISMISS_REMINDER
|
||||||
data = Uri.parse(habit.uriString)
|
data = Uri.parse(habit.uriString)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun removeRepetition(habit: Habit): PendingIntent =
|
fun removeRepetition(habit: Habit, timestamp: Timestamp?): PendingIntent =
|
||||||
getBroadcast(
|
getBroadcast(
|
||||||
context,
|
context,
|
||||||
3,
|
3,
|
||||||
Intent(context, WidgetReceiver::class.java).apply {
|
Intent(context, WidgetReceiver::class.java).apply {
|
||||||
action = WidgetReceiver.ACTION_REMOVE_REPETITION
|
action = WidgetReceiver.ACTION_REMOVE_REPETITION
|
||||||
data = Uri.parse(habit.uriString)
|
data = Uri.parse(habit.uriString)
|
||||||
|
if (timestamp != null) putExtra("timestamp", timestamp.unixTime)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun showHabit(habit: Habit): PendingIntent =
|
fun showHabit(habit: Habit): PendingIntent =
|
||||||
@@ -83,7 +85,7 @@ class PendingIntentFactory
|
|||||||
habit
|
habit
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.getPendingIntent(0, FLAG_UPDATE_CURRENT)!!
|
.getPendingIntent(0, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)!!
|
||||||
|
|
||||||
fun showReminder(
|
fun showReminder(
|
||||||
habit: Habit,
|
habit: Habit,
|
||||||
@@ -99,7 +101,7 @@ class PendingIntentFactory
|
|||||||
putExtra("timestamp", timestamp)
|
putExtra("timestamp", timestamp)
|
||||||
putExtra("reminderTime", reminderTime)
|
putExtra("reminderTime", reminderTime)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun snoozeNotification(habit: Habit): PendingIntent =
|
fun snoozeNotification(habit: Habit): PendingIntent =
|
||||||
@@ -110,7 +112,7 @@ class PendingIntentFactory
|
|||||||
data = Uri.parse(habit.uriString)
|
data = Uri.parse(habit.uriString)
|
||||||
action = ReminderReceiver.ACTION_SNOOZE_REMINDER
|
action = ReminderReceiver.ACTION_SNOOZE_REMINDER
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun toggleCheckmark(habit: Habit, timestamp: Long?): PendingIntent =
|
fun toggleCheckmark(habit: Habit, timestamp: Long?): PendingIntent =
|
||||||
@@ -122,7 +124,7 @@ class PendingIntentFactory
|
|||||||
action = WidgetReceiver.ACTION_TOGGLE_REPETITION
|
action = WidgetReceiver.ACTION_TOGGLE_REPETITION
|
||||||
if (timestamp != null) putExtra("timestamp", timestamp)
|
if (timestamp != null) putExtra("timestamp", timestamp)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun setNumericalValue(
|
fun setNumericalValue(
|
||||||
@@ -132,17 +134,17 @@ class PendingIntentFactory
|
|||||||
timestamp: Long?
|
timestamp: Long?
|
||||||
):
|
):
|
||||||
PendingIntent =
|
PendingIntent =
|
||||||
getBroadcast(
|
getBroadcast(
|
||||||
widgetContext,
|
widgetContext,
|
||||||
2,
|
2,
|
||||||
Intent(widgetContext, WidgetReceiver::class.java).apply {
|
Intent(widgetContext, WidgetReceiver::class.java).apply {
|
||||||
data = Uri.parse(habit.uriString)
|
data = Uri.parse(habit.uriString)
|
||||||
action = WidgetReceiver.ACTION_SET_NUMERICAL_VALUE
|
action = WidgetReceiver.ACTION_SET_NUMERICAL_VALUE
|
||||||
putExtra("numericalValue", numericalValue)
|
putExtra("numericalValue", numericalValue)
|
||||||
if (timestamp != null) putExtra("timestamp", timestamp)
|
if (timestamp != null) putExtra("timestamp", timestamp)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun updateWidgets(): PendingIntent =
|
fun updateWidgets(): PendingIntent =
|
||||||
getBroadcast(
|
getBroadcast(
|
||||||
@@ -151,6 +153,6 @@ class PendingIntentFactory
|
|||||||
Intent(context, WidgetReceiver::class.java).apply {
|
Intent(context, WidgetReceiver::class.java).apply {
|
||||||
action = WidgetReceiver.ACTION_UPDATE_WIDGETS_VALUE
|
action = WidgetReceiver.ACTION_UPDATE_WIDGETS_VALUE
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import android.app.NotificationChannel
|
|||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.BitmapFactory.decodeResource
|
import android.graphics.BitmapFactory.decodeResource
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Build.VERSION.SDK_INT
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -107,7 +106,7 @@ class AndroidNotificationTray
|
|||||||
val removeRepetitionAction = Action(
|
val removeRepetitionAction = Action(
|
||||||
R.drawable.ic_action_cancel,
|
R.drawable.ic_action_cancel,
|
||||||
context.getString(R.string.no),
|
context.getString(R.string.no),
|
||||||
pendingIntents.removeRepetition(habit)
|
pendingIntents.removeRepetition(habit, timestamp)
|
||||||
)
|
)
|
||||||
|
|
||||||
val enterAction = Action(
|
val enterAction = Action(
|
||||||
@@ -150,16 +149,15 @@ class AndroidNotificationTray
|
|||||||
if (!disableSound)
|
if (!disableSound)
|
||||||
builder.setSound(ringtoneManager.getURI())
|
builder.setSound(ringtoneManager.getURI())
|
||||||
|
|
||||||
if (preferences.shouldMakeNotificationsLed())
|
if (SDK_INT < Build.VERSION_CODES.S) {
|
||||||
builder.setLights(Color.RED, 1000, 1000)
|
val snoozeAction = Action(
|
||||||
|
R.drawable.ic_action_snooze,
|
||||||
val snoozeAction = Action(
|
context.getString(R.string.snooze),
|
||||||
R.drawable.ic_action_snooze,
|
pendingIntents.snoozeNotification(habit)
|
||||||
context.getString(R.string.snooze),
|
)
|
||||||
pendingIntents.snoozeNotification(habit)
|
wearableExtender.addAction(snoozeAction)
|
||||||
)
|
builder.addAction(snoozeAction)
|
||||||
wearableExtender.addAction(snoozeAction)
|
}
|
||||||
builder.addAction(snoozeAction)
|
|
||||||
|
|
||||||
builder.extend(wearableExtender)
|
builder.extend(wearableExtender)
|
||||||
return builder.build()
|
return builder.build()
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import android.widget.AdapterView.OnItemClickListener
|
|||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.android.datetimepicker.time.RadialPickerLayout
|
import com.android.datetimepicker.time.RadialPickerLayout
|
||||||
import com.android.datetimepicker.time.TimePickerDialog
|
import com.android.datetimepicker.time.TimePickerDialog
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
@@ -35,14 +36,13 @@ import org.isoron.uhabits.core.models.Habit
|
|||||||
import org.isoron.uhabits.core.ui.ThemeSwitcher.Companion.THEME_LIGHT
|
import org.isoron.uhabits.core.ui.ThemeSwitcher.Companion.THEME_LIGHT
|
||||||
import org.isoron.uhabits.receivers.ReminderController
|
import org.isoron.uhabits.receivers.ReminderController
|
||||||
import org.isoron.uhabits.utils.SystemUtils
|
import org.isoron.uhabits.utils.SystemUtils
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
|
|
||||||
class SnoozeDelayPickerActivity : FragmentActivity(), OnItemClickListener {
|
class SnoozeDelayPickerActivity : FragmentActivity(), OnItemClickListener {
|
||||||
private var habit: Habit? = null
|
private var habit: Habit? = null
|
||||||
private var reminderController: ReminderController? = null
|
private var reminderController: ReminderController? = null
|
||||||
private var dialog: AlertDialog? = null
|
private var dialog: AlertDialog? = null
|
||||||
private var color: Int = 0
|
private var androidColor: Int = 0
|
||||||
|
|
||||||
override fun onCreate(bundle: Bundle?) {
|
override fun onCreate(bundle: Bundle?) {
|
||||||
super.onCreate(bundle)
|
super.onCreate(bundle)
|
||||||
@@ -63,7 +63,7 @@ class SnoozeDelayPickerActivity : FragmentActivity(), OnItemClickListener {
|
|||||||
habit = appComponent.habitList.getById(ContentUris.parseId(data))
|
habit = appComponent.habitList.getById(ContentUris.parseId(data))
|
||||||
}
|
}
|
||||||
if (habit == null) finish()
|
if (habit == null) finish()
|
||||||
color = habit!!.color.toThemedAndroidColor(this)
|
androidColor = themeSwitcher.currentTheme.color(habit!!.color).toInt()
|
||||||
reminderController = appComponent.reminderController
|
reminderController = appComponent.reminderController
|
||||||
dialog = AlertDialog.Builder(this)
|
dialog = AlertDialog.Builder(this)
|
||||||
.setTitle(R.string.select_snooze_delay)
|
.setTitle(R.string.select_snooze_delay)
|
||||||
@@ -85,7 +85,7 @@ class SnoozeDelayPickerActivity : FragmentActivity(), OnItemClickListener {
|
|||||||
calendar[Calendar.HOUR_OF_DAY],
|
calendar[Calendar.HOUR_OF_DAY],
|
||||||
calendar[Calendar.MINUTE],
|
calendar[Calendar.MINUTE],
|
||||||
DateFormat.is24HourFormat(this),
|
DateFormat.is24HourFormat(this),
|
||||||
color
|
androidColor
|
||||||
)
|
)
|
||||||
dialog.show(supportFragmentManager, "timePicker")
|
dialog.show(supportFragmentManager, "timePicker")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,8 +90,9 @@ class SharedPreferencesStorage
|
|||||||
preferences.isMidnightDelayEnabled = getBoolean(key, false)
|
preferences.isMidnightDelayEnabled = getBoolean(key, false)
|
||||||
"pref_sticky_notifications" ->
|
"pref_sticky_notifications" ->
|
||||||
preferences.setNotificationsSticky(getBoolean(key, false))
|
preferences.setNotificationsSticky(getBoolean(key, false))
|
||||||
"pref_led_notifications" ->
|
"pref_unknown_enabled" -> {
|
||||||
preferences.setNotificationsLed(getBoolean(key, false))
|
preferences.areQuestionMarksEnabled = getBoolean(key, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
|
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import android.content.BroadcastReceiver
|
|||||||
import android.content.ContentUris
|
import android.content.ContentUris
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
@@ -76,8 +78,21 @@ class ReminderReceiver : BroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
ACTION_SNOOZE_REMINDER -> {
|
ACTION_SNOOZE_REMINDER -> {
|
||||||
if (habit == null) return
|
if (habit == null) return
|
||||||
Log.d("ReminderReceiver", String.format("onSnoozePressed habit=%d", habit.id))
|
if (SDK_INT < Build.VERSION_CODES.S) {
|
||||||
reminderController.onSnoozePressed(habit, context)
|
Log.d(
|
||||||
|
"ReminderReceiver",
|
||||||
|
String.format("onSnoozePressed habit=%d", habit.id)
|
||||||
|
)
|
||||||
|
reminderController.onSnoozePressed(habit, context)
|
||||||
|
} else {
|
||||||
|
Log.w(
|
||||||
|
"ReminderReceiver",
|
||||||
|
String.format(
|
||||||
|
"onSnoozePressed habit=%d, should be deactivated in recent versions.",
|
||||||
|
habit.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Intent.ACTION_BOOT_COMPLETED -> {
|
Intent.ACTION_BOOT_COMPLETED -> {
|
||||||
Log.d("ReminderReceiver", "onBootCompleted")
|
Log.d("ReminderReceiver", "onBootCompleted")
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ package org.isoron.uhabits.utils
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.util.Log
|
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
|
|
||||||
object PaletteUtils {
|
object PaletteUtils {
|
||||||
@@ -29,16 +28,6 @@ object PaletteUtils {
|
|||||||
fun getAndroidTestColor(index: Int) = PaletteColor(index).toFixedAndroidColor()
|
fun getAndroidTestColor(index: Int) = PaletteColor(index).toFixedAndroidColor()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PaletteColor.toThemedAndroidColor(context: Context): Int {
|
|
||||||
val palette = StyledResources(context).getPalette()
|
|
||||||
return if (paletteIndex in palette.indices) {
|
|
||||||
palette[paletteIndex]
|
|
||||||
} else {
|
|
||||||
Log.w("ColorHelper", "Invalid color: $paletteIndex. Returning default.")
|
|
||||||
palette[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun PaletteColor.toFixedAndroidColor(): Int {
|
fun PaletteColor.toFixedAndroidColor(): Int {
|
||||||
return intArrayOf(
|
return intArrayOf(
|
||||||
Color.parseColor("#D32F2F"), // 0 red
|
Color.parseColor("#D32F2F"), // 0 red
|
||||||
|
|||||||
@@ -22,14 +22,13 @@ import android.app.Activity
|
|||||||
import android.app.KeyguardManager
|
import android.app.KeyguardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
|
|
||||||
object SystemUtils {
|
object SystemUtils {
|
||||||
val isAndroidOOrLater: Boolean
|
|
||||||
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
|
||||||
|
|
||||||
fun unlockScreen(activity: Activity) {
|
fun unlockScreen(activity: Activity) {
|
||||||
if (isAndroidOOrLater) {
|
if (SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val km = activity.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
val km = activity.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||||
km.requestDismissKeyguard(activity, null)
|
km.requestDismissKeyguard(activity, null)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -20,16 +20,22 @@
|
|||||||
package org.isoron.uhabits.utils
|
package org.isoron.uhabits.utils
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.app.Dialog
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Canvas
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
import android.os.SystemClock
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
import android.view.WindowManager
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM
|
import android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM
|
||||||
import android.widget.RelativeLayout.ALIGN_PARENT_TOP
|
import android.widget.RelativeLayout.ALIGN_PARENT_TOP
|
||||||
@@ -40,8 +46,12 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
fun RelativeLayout.addBelow(
|
fun RelativeLayout.addBelow(
|
||||||
@@ -157,6 +167,7 @@ fun View.setupToolbar(
|
|||||||
toolbar: Toolbar,
|
toolbar: Toolbar,
|
||||||
title: String,
|
title: String,
|
||||||
color: PaletteColor,
|
color: PaletteColor,
|
||||||
|
theme: Theme,
|
||||||
displayHomeAsUpEnabled: Boolean = true,
|
displayHomeAsUpEnabled: Boolean = true,
|
||||||
) {
|
) {
|
||||||
toolbar.elevation = InterfaceUtils.dpToPixels(context, 2f)
|
toolbar.elevation = InterfaceUtils.dpToPixels(context, 2f)
|
||||||
@@ -165,7 +176,7 @@ fun View.setupToolbar(
|
|||||||
val toolbarColor = if (!res.getBoolean(R.attr.useHabitColorAsPrimary)) {
|
val toolbarColor = if (!res.getBoolean(R.attr.useHabitColorAsPrimary)) {
|
||||||
StyledResources(context).getColor(R.attr.colorPrimary)
|
StyledResources(context).getColor(R.attr.colorPrimary)
|
||||||
} else {
|
} else {
|
||||||
color.toThemedAndroidColor(context)
|
theme.color(color).toInt()
|
||||||
}
|
}
|
||||||
val darkerColor = ColorUtils.mixColors(toolbarColor, Color.BLACK, 0.75f)
|
val darkerColor = ColorUtils.mixColors(toolbarColor, Color.BLACK, 0.75f)
|
||||||
toolbar.background = ColorDrawable(toolbarColor)
|
toolbar.background = ColorDrawable(toolbarColor)
|
||||||
@@ -175,6 +186,13 @@ fun View.setupToolbar(
|
|||||||
activity.supportActionBar?.setDisplayHomeAsUpEnabled(displayHomeAsUpEnabled)
|
activity.supportActionBar?.setDisplayHomeAsUpEnabled(displayHomeAsUpEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun View.currentTheme(): Theme {
|
||||||
|
val component = (context.applicationContext as HabitsApplication).component
|
||||||
|
val themeSwitcher = AndroidThemeSwitcher(context, component.preferences)
|
||||||
|
themeSwitcher.apply()
|
||||||
|
return themeSwitcher.currentTheme
|
||||||
|
}
|
||||||
|
|
||||||
fun Int.toMeasureSpec(mode: Int) =
|
fun Int.toMeasureSpec(mode: Int) =
|
||||||
View.MeasureSpec.makeMeasureSpec(this, mode)
|
View.MeasureSpec.makeMeasureSpec(this, mode)
|
||||||
|
|
||||||
@@ -187,5 +205,33 @@ fun View.dim(id: Int) = InterfaceUtils.getDimension(context, id)
|
|||||||
fun View.sp(value: Float) = InterfaceUtils.spToPixels(context, value)
|
fun View.sp(value: Float) = InterfaceUtils.spToPixels(context, value)
|
||||||
fun View.dp(value: Float) = InterfaceUtils.dpToPixels(context, value)
|
fun View.dp(value: Float) = InterfaceUtils.dpToPixels(context, value)
|
||||||
fun View.str(id: Int) = resources.getString(id)
|
fun View.str(id: Int) = resources.getString(id)
|
||||||
|
|
||||||
|
fun View.drawNotesIndicator(canvas: Canvas, color: Int, size: Float, notes: String) {
|
||||||
|
val pNotesIndicator = Paint()
|
||||||
|
pNotesIndicator.color = color
|
||||||
|
if (notes.isNotBlank()) {
|
||||||
|
val cy = 0.8f * size
|
||||||
|
canvas.drawCircle(width.toFloat() - cy, cy, 8f, pNotesIndicator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val View.sres: StyledResources
|
val View.sres: StyledResources
|
||||||
get() = StyledResources(context)
|
get() = StyledResources(context)
|
||||||
|
|
||||||
|
fun Dialog.dimBehind() {
|
||||||
|
window?.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
|
||||||
|
window?.setDimAmount(0.5f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.requestFocusWithKeyboard() {
|
||||||
|
// For some reason, Android does not open the soft keyboard by default when view.requestFocus
|
||||||
|
// is called. Several online solutions suggest using InputMethodManager, but these solutions
|
||||||
|
// are not reliable; sometimes the keyboard does not show, and sometimes it does not go away
|
||||||
|
// after focus is lost. Here, we simulate a click on the view, which triggers the keyboard.
|
||||||
|
// Based on: https://stackoverflow.com/a/7699556
|
||||||
|
postDelayed({
|
||||||
|
val time = SystemClock.uptimeMillis()
|
||||||
|
dispatchTouchEvent(MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
|
||||||
|
dispatchTouchEvent(MotionEvent.obtain(time, time, MotionEvent.ACTION_UP, 0f, 0f, 0))
|
||||||
|
}, 250)
|
||||||
|
}
|
||||||
|
|||||||