Compare commits
197 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 64c4367706 | |||
| c08702341a | |||
| abb8c09bc9 | |||
| 87255ceb25 | |||
| aedbfded0f | |||
| 779c0040bd | |||
| 7bba6a887f | |||
| 52a1d19793 | |||
| 35e346e7ea | |||
| 3102158bdb | |||
| 7afa65a53c | |||
| 2e5533d480 | |||
| b29c7e695a | |||
| 7df745eb82 | |||
| c0d518ca4d | |||
| eeff14a2ca | |||
| af3b18f518 | |||
| 3811f4b339 | |||
| 50a8aece6f | |||
| 2f66cfc417 | |||
| 132ce36a57 | |||
| 147f010d1b | |||
| afc91ed89a | |||
| 684c37d167 | |||
| 8b296ce2ae | |||
| e2e9e2e27a | |||
| 8b2f31c44a | |||
| 83db9fe2f2 | |||
| 9891139b5a | |||
| cbc8cbfea3 | |||
| 96c46b655d | |||
| 0de52d4fa3 | |||
| 16d85dc4c7 | |||
| c51b1fd399 | |||
| eb8dd1d450 | |||
| 357646152f | |||
| ae5be202b2 | |||
| cc3e2153d0 | |||
| 7433a2413d | |||
| 1cd8eb6849 | |||
| 7f009e2365 | |||
| 4cec7d7367 | |||
| f59c0bd7ff | |||
| 2d2bb8b4ed | |||
| e476096ae1 | |||
| e2c814a982 | |||
| 4fcaa68baf | |||
| 02e45dbe27 | |||
| bf6562f854 | |||
| 3c927e009a | |||
| a9bcae0f2f | |||
| bc54f3f916 | |||
| 5763d76167 | |||
| 86b66b1388 | |||
| 5e0422848b | |||
| a67a3297b1 | |||
| b2f2d4ad28 | |||
| 4dc8201dcb | |||
| f24e300b40 | |||
| 21157eaa19 | |||
| 0ea2c8e5c8 | |||
| 826491bc90 | |||
| 72f5ca5531 | |||
| f9da90a93a | |||
| 96c6a97ad0 | |||
| 20469789ea | |||
| abafffcff1 | |||
| d45a4445cc | |||
| 94f56a8869 | |||
| bc331d0a4d | |||
| 2ba7246e5f | |||
| 67b88c5012 | |||
| e0b637e84d | |||
| e14d73cf32 | |||
| 16b9584868 | |||
| c091116105 | |||
| 37e1a6233c | |||
| 6838a0f2d3 | |||
| 53e737c169 | |||
| 285f79ed95 | |||
| 4f65c8bef1 | |||
| 51b7cfc8fa | |||
| 55ed2585ea | |||
| 2c62e1578a | |||
| e3d96c0431 | |||
| b4c2d2237a | |||
| d14fbbd130 | |||
| e2c99d745e | |||
| dfe41176bc | |||
| 1ef4f8d456 | |||
| 6c810ee7a3 | |||
| 5115379fdd | |||
| d176ea91fb | |||
| 62df660079 | |||
| 790beb185a | |||
| 59c0af17d7 | |||
| 28dad560a6 | |||
| 9e410f8395 | |||
| 3656c51e95 | |||
| d3ebb4ff25 | |||
| 3c595bc79d | |||
| 1120f56dd4 | |||
| 2dfbcfcdb0 | |||
| 90a3964f0f | |||
| 6c05366f0f | |||
| dcc771abe7 | |||
| 743431ef67 | |||
| d5fc1a6886 | |||
| eeb0b109ae | |||
| ad391fa791 | |||
| e8bbae8ef9 | |||
| c9793df7c7 | |||
| 3c6114fc87 | |||
| 4ea4201b6a | |||
|
|
30b5b90091 | ||
| e6b7b8b590 | |||
| 2d675ed9b0 | |||
| 1db2f69f05 | |||
| 49a80faca3 | |||
| aa5df56437 | |||
| 581197be03 | |||
| dfe5c4954e | |||
| a0582b65d5 | |||
| 9edc3e12c9 | |||
| 7bb3db213f | |||
| e05a69b527 | |||
| d5df6ddcdb | |||
| 3016263750 | |||
| f9377e1768 | |||
| ae0dad9120 | |||
| be114bde7f | |||
| e7148abc2e | |||
| 2d87076a48 | |||
| 7da4ddf91b | |||
| e4e8d77acc | |||
| 7945a5faf9 | |||
| 3f7d25461d | |||
| 519737a1c3 | |||
| c3ff1fbe03 | |||
| d39e1978a2 | |||
| 536c095f23 | |||
| e693504183 | |||
| 7d9a94ae9e | |||
| 326cb8f73f | |||
| 6826bd1ddc | |||
| b5bc347624 | |||
| 55c058ff42 | |||
| e829bb8109 | |||
| 0921f9346e | |||
| eb017bf99b | |||
| 79b6ef8200 | |||
| 7ba62d6784 | |||
| f5e4a88415 | |||
| 075b7812eb | |||
| 3d42505fb9 | |||
| ffdc923268 | |||
| 9232378d04 | |||
| b20fd44cbc | |||
| ded8800017 | |||
| 606f66b8d9 | |||
| 1bb6cd405b | |||
| babf7d64f0 | |||
| dfbcf78dd7 | |||
| e01c668e4d | |||
| 2eef696027 | |||
| ddd10cacd1 | |||
| 0cdde4901e | |||
| f13e9b7362 | |||
| 558f72d7c5 | |||
| 6e493f55bc | |||
| 5186ab840a | |||
| 65fd82d888 | |||
| 866b62987c | |||
| 18abb2038f | |||
| f7f4b5eeb0 | |||
| 45a7433773 | |||
| 40420c3a77 | |||
| 988b39f2e5 | |||
| a0803966f9 | |||
| e4af662836 | |||
| e95adab422 | |||
| 012fed01eb | |||
| 4cf2b8072b | |||
| 196a79a88c | |||
| 4b7e2e79a7 | |||
| 9156bba267 | |||
| 1a18bb939d | |||
| 144524e53b | |||
| 3d1c53396c | |||
| 1930db3cd1 | |||
| a2c2a5531a | |||
| eee2605f74 | |||
|
|
9df0c9ae9e | ||
| e0533505c1 | |||
|
|
433894336c | ||
| 9061182301 | |||
| 8c45f52d56 |
1
.gitignore
vendored
@@ -33,3 +33,4 @@ build/
|
|||||||
*.iml
|
*.iml
|
||||||
|
|
||||||
art/
|
art/
|
||||||
|
*.actual.png
|
||||||
|
|||||||
23
CHANGELOG.md
@@ -1,8 +1,27 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
### 1.4.0 (April 4, 2016)
|
||||||
|
|
||||||
|
* Ability to import data from third-party apps
|
||||||
|
* Ability to save and restore full database backup
|
||||||
|
* Show more information on streak chart
|
||||||
|
* Simplify interface for creating habits
|
||||||
|
* Add link to Frequently Asked Questions (FAQ)
|
||||||
|
* Reduce app loading time and lag on widgets
|
||||||
|
* Generate bug reports on crash and from settings screen
|
||||||
|
* Disable vibration according to phone settings
|
||||||
|
* Add Czech translation
|
||||||
|
* Fix wrong month names for some languages
|
||||||
|
|
||||||
|
### 1.3.3 (March 20, 2016)
|
||||||
|
|
||||||
|
* Add Spanish and Korean translations
|
||||||
|
* Make small corrections to other translations
|
||||||
|
* Fix incorrect date in history calendar
|
||||||
|
|
||||||
### 1.3.2 (March 18, 2016)
|
### 1.3.2 (March 18, 2016)
|
||||||
|
|
||||||
* Arabic, Italian, Polish, Russian and Swedish translations
|
* Add Arabic, Italian, Polish, Russian and Swedish translations
|
||||||
* Minor fixes to German and French translations
|
* Minor fixes to German and French translations
|
||||||
* Minor bug fixes
|
* Minor bug fixes
|
||||||
|
|
||||||
@@ -16,7 +35,7 @@
|
|||||||
|
|
||||||
* New frequency plot: view total repetitions per day of week
|
* New frequency plot: view total repetitions per day of week
|
||||||
* New history editor: put checkmarks in the past
|
* New history editor: put checkmarks in the past
|
||||||
* German, French and Japanese translations
|
* Add German, French and Japanese translations
|
||||||
* Add about screen, with credits to all contributors
|
* Add about screen, with credits to all contributors
|
||||||
* Fix small bug that prevented habit from being reordered
|
* Fix small bug that prevented habit from being reordered
|
||||||
* Fix small bug caused by rotating the device
|
* Fix small bug caused by rotating the device
|
||||||
|
|||||||
19
NOTICE.md
@@ -89,3 +89,22 @@ Material design icons are the official icon set from Google that are designed
|
|||||||
under the material design guidelines. Available under the Creative Common
|
under the material design guidelines. Available under the Creative Common
|
||||||
Attribution 4.0 International License (CC-BY 4.0).
|
Attribution 4.0 International License (CC-BY 4.0).
|
||||||
|
|
||||||
|
### Android Flow Layout
|
||||||
|
|
||||||
|
<https://github.com/ApmeM/android-flowlayout>
|
||||||
|
|
||||||
|
Extended linear layout that wrap its content when there is no place in the current line.
|
||||||
|
|
||||||
|
Copyright 2011, Artem Votincev (apmem.org)
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
use this file except in compliance with the License. You may obtain a copy
|
||||||
|
of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
License for the specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
15
README.md
@@ -1,12 +1,20 @@
|
|||||||
# Loop Habit Tracker
|
# Loop Habit Tracker
|
||||||
|
<a href="https://circleci.com/gh/iSoron/uhabits/tree/dev">
|
||||||
|
<img src="https://img.shields.io/circleci/project/iSoron/uhabits/dev.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://codecov.io/github/iSoron/uhabits?branch=dev">
|
||||||
|
<img src="https://img.shields.io/codecov/c/github/iSoron/uhabits.svg" alt="Coverage via Codecov" />
|
||||||
|
</a>
|
||||||
|
|
||||||
Loop is a simple Android app that helps you create and maintain good habits,
|
Loop is a simple Android app that helps you create and maintain good habits,
|
||||||
allowing you to achieve your long-term goals. Detailed graphs and statistics
|
allowing you to achieve your long-term goals. Detailed graphs and statistics
|
||||||
show you how your habits improved over time. It is completely ad-free and open
|
show you how your habits improved over time. It is completely ad-free and open
|
||||||
source.
|
source.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
<a href="https://play.google.com/store/apps/details?id=org.isoron.uhabits&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-AC-global-none-all-co-pr-py-PartBadges-Oct1515-1"><img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/apps/en-play-badge-border.png" height="75px"/></a>
|
<a href="https://play.google.com/store/apps/details?id=org.isoron.uhabits&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-AC-global-none-all-co-pr-py-PartBadges-Oct1515-1"><img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/apps/en-play-badge-border.png" height="75px"/></a>
|
||||||
<a href="http://f-droid.org/app/org.isoron.uhabits"><img alt="Git if on F-Droid" src="http://i.imgur.com/baSPE7X.png" height="75px"/></a>
|
<a href="http://f-droid.org/app/org.isoron.uhabits"><img alt="Git if on F-Droid" src="http://i.imgur.com/baSPE7X.png" height="75px"/></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -65,9 +73,14 @@ contribute, even if you are not a software developer.
|
|||||||
to improve it. You can either use the link inside the app, or open an issue
|
to improve it. You can either use the link inside the app, or open an issue
|
||||||
at GitHub.
|
at GitHub.
|
||||||
|
|
||||||
|
* **Spread the word.** If you like the app, share it with your family, friends
|
||||||
|
and colleagues. You can also rate and review the app on Google Play Store, to help
|
||||||
|
other users find it more easily.
|
||||||
|
|
||||||
* **Translate the app into your own language.** If you are not a native English
|
* **Translate the app into your own language.** If you are not a native English
|
||||||
speaker, and would like to see the app translated into your own language,
|
speaker, and would like to see the app translated into your own language,
|
||||||
please join our [open translation project at POEditor][poedit].
|
please join our [open translation project at POEditor][poedit]. If the translation
|
||||||
|
is already completed, you are also very welcome to join and proofread it.
|
||||||
|
|
||||||
* **Write some code.** If you are an Android developer, you are very welcome to
|
* **Write some code.** If you are an Android developer, you are very welcome to
|
||||||
contribute with code. Please, see the [developer guidelines][dev-guide] for more details.
|
contribute with code. Please, see the [developer guidelines][dev-guide] for more details.
|
||||||
|
|||||||
@@ -2,14 +2,18 @@ apply plugin: 'com.android.application'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 23
|
compileSdkVersion 23
|
||||||
buildToolsVersion "21.1.2"
|
buildToolsVersion "23.0.1"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "org.isoron.uhabits"
|
applicationId "org.isoron.uhabits"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 23
|
targetSdkVersion 23
|
||||||
|
|
||||||
|
buildConfigField "Integer", "databaseVersion", "13"
|
||||||
|
buildConfigField "String", "databaseFilename", "\"uhabits.db\""
|
||||||
|
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
//testInstrumentationRunnerArgument "size", "small"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -30,6 +34,8 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile 'com.android.support:support-v4:23.1.1'
|
compile 'com.android.support:support-v4:23.1.1'
|
||||||
compile 'com.github.paolorotolo:appintro:3.4.0'
|
compile 'com.github.paolorotolo:appintro:3.4.0'
|
||||||
|
compile 'org.apmem.tools:layouts:1.10@aar'
|
||||||
|
compile 'com.opencsv:opencsv:3.7'
|
||||||
compile project(':libs:drag-sort-listview:library')
|
compile project(':libs:drag-sort-listview:library')
|
||||||
compile files('libs/ActiveAndroid.jar')
|
compile files('libs/ActiveAndroid.jar')
|
||||||
|
|
||||||
@@ -40,3 +46,13 @@ dependencies {
|
|||||||
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'
|
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
task grantAnimationPermission(type: Exec, dependsOn: 'installDebug') {
|
||||||
|
commandLine "adb shell pm grant org.isoron.uhabits android.permission.SET_ANIMATION_SCALE".split(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.whenTaskAdded { task ->
|
||||||
|
if (task.name.startsWith('connected')) {
|
||||||
|
task.dependsOn grantAnimationPermission
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/src/androidTest/assets/habitbull.csv
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
HabitName,HabitDescription,HabitCategory,CalendarDate,Value,CommentText
|
||||||
|
Breed dragons,with love and fire,Diet & Food,2016-03-18,1,
|
||||||
|
Breed dragons,with love and fire,Diet & Food,2016-03-19,1,
|
||||||
|
Breed dragons,with love and fire,Diet & Food,2016-03-21,1,
|
||||||
|
Reduce sleep,only 2 hours per day,Time Management,2016-03-15,1,
|
||||||
|
Reduce sleep,only 2 hours per day,Time Management,2016-03-16,1,
|
||||||
|
Reduce sleep,only 2 hours per day,Time Management,2016-03-17,1,
|
||||||
|
Reduce sleep,only 2 hours per day,Time Management,2016-03-21,1,
|
||||||
|
No-arms pushup,Become like water my friend!,Fitness,2016-03-15,1,
|
||||||
|
No-arms pushup,Become like water my friend!,Fitness,2016-03-16,1,
|
||||||
|
No-arms pushup,Become like water my friend!,Fitness,2016-03-18,1,
|
||||||
|
No-arms pushup,Become like water my friend!,Fitness,2016-03-21,1,
|
||||||
|
No-arms pushup,Become like water my friend!,Fitness,2016-03-15,1,
|
||||||
|
No-arms pushup,Become like water my friend!,Fitness,2016-03-16,1,
|
||||||
|
No-arms pushup,Become like water my friend!,Fitness,2016-03-18,1,
|
||||||
|
No-arms pushup,Become like water my friend!,Fitness,2016-03-21,1,
|
||||||
|
Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-15,1,
|
||||||
|
Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-17,1,
|
||||||
|
Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-21,1,
|
||||||
|
BIN
app/src/androidTest/assets/icon.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
app/src/androidTest/assets/loop.db
Normal file
5
app/src/androidTest/assets/pull_failed
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
P=/sdcard/Android/data/org.isoron.uhabits/cache/Failed/
|
||||||
|
|
||||||
|
adb pull $P Failed/
|
||||||
|
adb shell rm -r $P
|
||||||
BIN
app/src/androidTest/assets/rewire.db
Normal file
BIN
app/src/androidTest/assets/tickmate.db
Normal file
BIN
app/src/androidTest/assets/views-v21/CheckmarkView/checked.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 24 KiB |
BIN
app/src/androidTest/assets/views-v21/CheckmarkView/unchecked.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
app/src/androidTest/assets/views/CheckmarkView/checked.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
BIN
app/src/androidTest/assets/views/CheckmarkView/large_size.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
app/src/androidTest/assets/views/CheckmarkView/unchecked.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
app/src/androidTest/assets/views/HabitFrequencyView/render.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
BIN
app/src/androidTest/assets/views/HabitHistoryView/render.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 31 KiB |
BIN
app/src/androidTest/assets/views/HabitScoreView/render.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
BIN
app/src/androidTest/assets/views/HabitScoreView/renderYearly.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
app/src/androidTest/assets/views/HabitStreakView/render.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 14 KiB |
BIN
app/src/androidTest/assets/views/NumberView/render.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
BIN
app/src/androidTest/assets/views/NumberView/renderLongLabel.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
app/src/androidTest/assets/views/RingView/render.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
BIN
app/src/androidTest/assets/views/RingView/renderLongLabel.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
66
app/src/androidTest/java/org/isoron/uhabits/BaseTest.java
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.tasks.BaseTask;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
public class BaseTest
|
||||||
|
{
|
||||||
|
protected Context testContext;
|
||||||
|
protected Context targetContext;
|
||||||
|
private static boolean isLooperPrepared;
|
||||||
|
|
||||||
|
public static final long FIXED_LOCAL_TIME = 1422172800000L; // 8:00am, January 25th, 2015 (UTC)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
if(!isLooperPrepared)
|
||||||
|
{
|
||||||
|
Looper.prepare();
|
||||||
|
isLooperPrepared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetContext = InstrumentationRegistry.getTargetContext();
|
||||||
|
testContext = InstrumentationRegistry.getContext();
|
||||||
|
|
||||||
|
DateHelper.setFixedLocalTime(FIXED_LOCAL_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void waitForAsyncTasks() throws InterruptedException, TimeoutException
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
|
||||||
|
{
|
||||||
|
Thread.sleep(1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseTask.waitForTasks(10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
package org.isoron.uhabits;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.test.InstrumentationRegistry;
|
|
||||||
import android.support.test.espresso.NoMatchingViewException;
|
|
||||||
import android.support.test.espresso.intent.rule.IntentsTestRule;
|
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
import android.test.suitebuilder.annotation.LargeTest;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import static android.support.test.espresso.Espresso.onData;
|
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
|
||||||
import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
|
|
||||||
import static android.support.test.espresso.Espresso.pressBack;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.click;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.longClick;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.scrollTo;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.swipeLeft;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.swipeRight;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.swipeUp;
|
|
||||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
|
||||||
import static org.hamcrest.Matchers.allOf;
|
|
||||||
import static org.hamcrest.Matchers.endsWith;
|
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
|
||||||
import static org.hamcrest.Matchers.is;
|
|
||||||
import static org.isoron.uhabits.HabitMatchers.withName;
|
|
||||||
import static org.isoron.uhabits.HabitViewActions.clickAtRandomLocations;
|
|
||||||
import static org.isoron.uhabits.HabitViewActions.toggleAllCheckmarks;
|
|
||||||
import static org.isoron.uhabits.MainActivityActions.addHabit;
|
|
||||||
import static org.isoron.uhabits.MainActivityActions.assertHabitExists;
|
|
||||||
import static org.isoron.uhabits.MainActivityActions.assertHabitsDontExist;
|
|
||||||
import static org.isoron.uhabits.MainActivityActions.assertHabitsExist;
|
|
||||||
import static org.isoron.uhabits.MainActivityActions.clickActionModeMenuItem;
|
|
||||||
import static org.isoron.uhabits.MainActivityActions.deleteHabit;
|
|
||||||
import static org.isoron.uhabits.MainActivityActions.deleteHabits;
|
|
||||||
import static org.isoron.uhabits.MainActivityActions.selectHabit;
|
|
||||||
import static org.isoron.uhabits.MainActivityActions.selectHabits;
|
|
||||||
import static org.isoron.uhabits.MainActivityActions.typeHabitData;
|
|
||||||
import static org.isoron.uhabits.ShowHabitActivityActions.openHistoryEditor;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
@LargeTest
|
|
||||||
public class MainTest
|
|
||||||
{
|
|
||||||
@Rule
|
|
||||||
public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(
|
|
||||||
MainActivity.class);
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void skipTutorial()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
onView(allOf(withClassName(endsWith("AppCompatImageButton")),
|
|
||||||
isDisplayed())).perform(click());
|
|
||||||
}
|
|
||||||
catch (NoMatchingViewException e)
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testArchiveHabits()
|
|
||||||
{
|
|
||||||
List<String> names = new LinkedList<>();
|
|
||||||
Context context = InstrumentationRegistry.getTargetContext();
|
|
||||||
|
|
||||||
for(int i = 0; i < 3; i++)
|
|
||||||
names.add(addHabit());
|
|
||||||
|
|
||||||
selectHabits(names);
|
|
||||||
|
|
||||||
clickActionModeMenuItem(R.string.archive);
|
|
||||||
assertHabitsDontExist(names);
|
|
||||||
|
|
||||||
openActionBarOverflowOrOptionsMenu(context);
|
|
||||||
onView(withText(R.string.show_archived))
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
assertHabitsExist(names);
|
|
||||||
selectHabits(names);
|
|
||||||
clickActionModeMenuItem(R.string.unarchive);
|
|
||||||
|
|
||||||
openActionBarOverflowOrOptionsMenu(context);
|
|
||||||
onView(withText(R.string.show_archived))
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
assertHabitsExist(names);
|
|
||||||
deleteHabits(names);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddInvalidHabit()
|
|
||||||
{
|
|
||||||
onView(withId(R.id.action_add))
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
typeHabitData("", "", "15", "7");
|
|
||||||
|
|
||||||
onView(withId(R.id.buttonSave)).perform(click());
|
|
||||||
onView(withId(R.id.input_name)).check(matches(isDisplayed()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddHabitAndViewStats()
|
|
||||||
{
|
|
||||||
String name = addHabit(true);
|
|
||||||
|
|
||||||
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
|
||||||
.onChildView(withId(R.id.llButtons))
|
|
||||||
.perform(toggleAllCheckmarks());
|
|
||||||
|
|
||||||
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
|
||||||
.onChildView(withId(R.id.label))
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
onView(withId(R.id.scoreView))
|
|
||||||
.perform(swipeRight());
|
|
||||||
|
|
||||||
onView(withId(R.id.punchcardView))
|
|
||||||
.perform(scrollTo());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEditHabit()
|
|
||||||
{
|
|
||||||
String name = addHabit();
|
|
||||||
|
|
||||||
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
|
||||||
.onChildView(withId(R.id.label))
|
|
||||||
.perform(longClick());
|
|
||||||
|
|
||||||
clickActionModeMenuItem(R.string.edit);
|
|
||||||
|
|
||||||
String modifiedName = "Modified " + new Random().nextInt(10000);
|
|
||||||
typeHabitData(modifiedName, "", "1", "1");
|
|
||||||
|
|
||||||
onView(withId(R.id.buttonSave))
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
assertHabitExists(modifiedName);
|
|
||||||
|
|
||||||
selectHabit(modifiedName);
|
|
||||||
clickActionModeMenuItem(R.string.color_picker_default_title);
|
|
||||||
pressBack();
|
|
||||||
|
|
||||||
deleteHabit(modifiedName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEditHistory()
|
|
||||||
{
|
|
||||||
String name = addHabit();
|
|
||||||
|
|
||||||
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
|
||||||
.onChildView(withId(R.id.label))
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
openHistoryEditor();
|
|
||||||
onView(withClassName(endsWith("HabitHistoryView")))
|
|
||||||
.perform(clickAtRandomLocations(20));
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
onView(withId(R.id.historyView))
|
|
||||||
.perform(scrollTo(), swipeRight(), swipeLeft());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSettings()
|
|
||||||
{
|
|
||||||
Context context = InstrumentationRegistry.getContext();
|
|
||||||
openActionBarOverflowOrOptionsMenu(context);
|
|
||||||
onView(withText(R.string.settings)).perform(click());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAbout()
|
|
||||||
{
|
|
||||||
Context context = InstrumentationRegistry.getContext();
|
|
||||||
openActionBarOverflowOrOptionsMenu(context);
|
|
||||||
onView(withText(R.string.about)).perform(click());
|
|
||||||
onView(isRoot()).perform(swipeUp());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,12 +17,14 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits.ui;
|
||||||
|
|
||||||
|
import android.preference.Preference;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Adapter;
|
import android.widget.Adapter;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
|
|
||||||
|
import org.hamcrest.BaseMatcher;
|
||||||
import org.hamcrest.Description;
|
import org.hamcrest.Description;
|
||||||
import org.hamcrest.Matcher;
|
import org.hamcrest.Matcher;
|
||||||
import org.hamcrest.TypeSafeMatcher;
|
import org.hamcrest.TypeSafeMatcher;
|
||||||
@@ -76,4 +78,23 @@ public class HabitMatchers
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Matcher<?> isPreferenceWithText(final String text)
|
||||||
|
{
|
||||||
|
return (Matcher<?>) new BaseMatcher()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean matches(Object o)
|
||||||
|
{
|
||||||
|
if(!(o instanceof Preference)) return false;
|
||||||
|
return o.toString().contains(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describeTo(Description description)
|
||||||
|
{
|
||||||
|
description.appendText(String.format("is preference with text '%s'", text));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits.ui;
|
||||||
|
|
||||||
import android.support.test.espresso.UiController;
|
import android.support.test.espresso.UiController;
|
||||||
import android.support.test.espresso.ViewAction;
|
import android.support.test.espresso.ViewAction;
|
||||||
@@ -32,6 +32,7 @@ import android.widget.LinearLayout;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.hamcrest.Matcher;
|
import org.hamcrest.Matcher;
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
|
||||||
import java.security.InvalidParameterException;
|
import java.security.InvalidParameterException;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
@@ -17,11 +17,11 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits.ui;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.support.test.espresso.NoMatchingViewException;
|
||||||
import android.support.test.InstrumentationRegistry;
|
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -37,16 +37,20 @@ import static android.support.test.espresso.action.ViewActions.click;
|
|||||||
import static android.support.test.espresso.action.ViewActions.longClick;
|
import static android.support.test.espresso.action.ViewActions.longClick;
|
||||||
import static android.support.test.espresso.action.ViewActions.replaceText;
|
import static android.support.test.espresso.action.ViewActions.replaceText;
|
||||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
||||||
|
import static android.support.test.espresso.matcher.RootMatchers.isPlatformPopup;
|
||||||
|
import static android.support.test.espresso.matcher.ViewMatchers.Visibility.VISIBLE;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
|
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||||
|
import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.isoron.uhabits.HabitMatchers.containsHabit;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
import static org.isoron.uhabits.HabitMatchers.withName;
|
import static org.isoron.uhabits.ui.HabitMatchers.containsHabit;
|
||||||
|
import static org.isoron.uhabits.ui.HabitMatchers.withName;
|
||||||
|
|
||||||
public class MainActivityActions
|
public class MainActivityActions
|
||||||
{
|
{
|
||||||
@@ -97,6 +101,20 @@ public class MainActivityActions
|
|||||||
.perform(replaceText(name));
|
.perform(replaceText(name));
|
||||||
onView(withId(R.id.input_description))
|
onView(withId(R.id.input_description))
|
||||||
.perform(replaceText(description));
|
.perform(replaceText(description));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
onView(allOf(withId(R.id.sFrequency), withEffectiveVisibility(VISIBLE)))
|
||||||
|
.perform(click());
|
||||||
|
onData(allOf(instanceOf(String.class), startsWith("Custom")))
|
||||||
|
.inRoot(isPlatformPopup())
|
||||||
|
.perform(click());
|
||||||
|
}
|
||||||
|
catch(NoMatchingViewException e)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
onView(withId(R.id.input_freq_num))
|
onView(withId(R.id.input_freq_num))
|
||||||
.perform(replaceText(num));
|
.perform(replaceText(num));
|
||||||
onView(withId(R.id.input_freq_den))
|
onView(withId(R.id.input_freq_den))
|
||||||
359
app/src/androidTest/java/org/isoron/uhabits/ui/MainTest.java
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Instrumentation;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
import android.support.test.espresso.NoMatchingViewException;
|
||||||
|
import android.support.test.espresso.intent.rule.IntentsTestRule;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.LargeTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.MainActivity;
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static android.support.test.espresso.Espresso.onData;
|
||||||
|
import static android.support.test.espresso.Espresso.onView;
|
||||||
|
import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
|
||||||
|
import static android.support.test.espresso.Espresso.pressBack;
|
||||||
|
import static android.support.test.espresso.action.ViewActions.click;
|
||||||
|
import static android.support.test.espresso.action.ViewActions.longClick;
|
||||||
|
import static android.support.test.espresso.action.ViewActions.scrollTo;
|
||||||
|
import static android.support.test.espresso.action.ViewActions.swipeLeft;
|
||||||
|
import static android.support.test.espresso.action.ViewActions.swipeRight;
|
||||||
|
import static android.support.test.espresso.action.ViewActions.swipeUp;
|
||||||
|
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
||||||
|
import static android.support.test.espresso.intent.Intents.intended;
|
||||||
|
import static android.support.test.espresso.intent.Intents.intending;
|
||||||
|
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction;
|
||||||
|
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
|
import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
|
||||||
|
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
|
||||||
|
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||||
|
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||||
|
import static org.hamcrest.Matchers.allOf;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.endsWith;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
|
import static org.isoron.uhabits.ui.HabitMatchers.isPreferenceWithText;
|
||||||
|
import static org.isoron.uhabits.ui.HabitMatchers.withName;
|
||||||
|
import static org.isoron.uhabits.ui.HabitViewActions.clickAtRandomLocations;
|
||||||
|
import static org.isoron.uhabits.ui.HabitViewActions.toggleAllCheckmarks;
|
||||||
|
import static org.isoron.uhabits.ui.MainActivityActions.addHabit;
|
||||||
|
import static org.isoron.uhabits.ui.MainActivityActions.assertHabitExists;
|
||||||
|
import static org.isoron.uhabits.ui.MainActivityActions.assertHabitsDontExist;
|
||||||
|
import static org.isoron.uhabits.ui.MainActivityActions.assertHabitsExist;
|
||||||
|
import static org.isoron.uhabits.ui.MainActivityActions.clickActionModeMenuItem;
|
||||||
|
import static org.isoron.uhabits.ui.MainActivityActions.deleteHabit;
|
||||||
|
import static org.isoron.uhabits.ui.MainActivityActions.deleteHabits;
|
||||||
|
import static org.isoron.uhabits.ui.MainActivityActions.selectHabit;
|
||||||
|
import static org.isoron.uhabits.ui.MainActivityActions.selectHabits;
|
||||||
|
import static org.isoron.uhabits.ui.MainActivityActions.typeHabitData;
|
||||||
|
import static org.isoron.uhabits.ui.ShowHabitActivityActions.openHistoryEditor;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@LargeTest
|
||||||
|
public class MainTest
|
||||||
|
{
|
||||||
|
private SystemHelper sys;
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(
|
||||||
|
MainActivity.class);
|
||||||
|
|
||||||
|
private Context targetContext;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
Context context = InstrumentationRegistry.getInstrumentation().getContext();
|
||||||
|
sys = new SystemHelper(context);
|
||||||
|
sys.disableAllAnimations();
|
||||||
|
sys.acquireWakeLock();
|
||||||
|
sys.unlockScreen();
|
||||||
|
|
||||||
|
targetContext = InstrumentationRegistry.getTargetContext();
|
||||||
|
|
||||||
|
Instrumentation.ActivityResult okResult = new Instrumentation.ActivityResult(
|
||||||
|
Activity.RESULT_OK, new Intent());
|
||||||
|
|
||||||
|
intending(hasAction(equalTo(Intent.ACTION_SEND))).respondWith(okResult);
|
||||||
|
intending(hasAction(equalTo(Intent.ACTION_SENDTO))).respondWith(okResult);
|
||||||
|
intending(hasAction(equalTo(Intent.ACTION_VIEW))).respondWith(okResult);
|
||||||
|
|
||||||
|
skipTutorial();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown()
|
||||||
|
{
|
||||||
|
sys.releaseWakeLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void skipTutorial()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
onView(allOf(withClassName(endsWith("AppCompatImageButton")),
|
||||||
|
isDisplayed())).perform(click());
|
||||||
|
}
|
||||||
|
catch (NoMatchingViewException e)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User opens the app, creates some habits, selects them, archives them, select 'show archived'
|
||||||
|
* on the menu, selects the previously archived habits and then deletes them.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testArchiveHabits()
|
||||||
|
{
|
||||||
|
List<String> names = new LinkedList<>();
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; i++)
|
||||||
|
names.add(addHabit());
|
||||||
|
|
||||||
|
selectHabits(names);
|
||||||
|
|
||||||
|
clickActionModeMenuItem(R.string.archive);
|
||||||
|
assertHabitsDontExist(names);
|
||||||
|
|
||||||
|
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||||
|
onView(withText(R.string.show_archived))
|
||||||
|
.perform(click());
|
||||||
|
|
||||||
|
assertHabitsExist(names);
|
||||||
|
selectHabits(names);
|
||||||
|
clickActionModeMenuItem(R.string.unarchive);
|
||||||
|
|
||||||
|
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||||
|
onView(withText(R.string.show_archived))
|
||||||
|
.perform(click());
|
||||||
|
|
||||||
|
assertHabitsExist(names);
|
||||||
|
deleteHabits(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User opens the app, clicks the add button, types some bogus information, tries to save,
|
||||||
|
* dialog displays an error.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAddInvalidHabit()
|
||||||
|
{
|
||||||
|
onView(withId(R.id.action_add))
|
||||||
|
.perform(click());
|
||||||
|
|
||||||
|
typeHabitData("", "", "15", "7");
|
||||||
|
|
||||||
|
onView(withId(R.id.buttonSave)).perform(click());
|
||||||
|
onView(withId(R.id.input_name)).check(matches(isDisplayed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User creates a habit, toggles a bunch of checkmarks, clicks the habit to open the statistics
|
||||||
|
* screen, scrolls down to some views, then scrolls the views backwards and forwards in time.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAddHabitAndViewStats() throws InterruptedException
|
||||||
|
{
|
||||||
|
String name = addHabit(true);
|
||||||
|
|
||||||
|
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
||||||
|
.onChildView(withId(R.id.llButtons))
|
||||||
|
.perform(toggleAllCheckmarks());
|
||||||
|
|
||||||
|
Thread.sleep(1200);
|
||||||
|
|
||||||
|
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
||||||
|
.onChildView(withId(R.id.label))
|
||||||
|
.perform(click());
|
||||||
|
|
||||||
|
onView(withId(R.id.scoreView))
|
||||||
|
.perform(scrollTo(), swipeRight());
|
||||||
|
|
||||||
|
onView(withId(R.id.punchcardView))
|
||||||
|
.perform(scrollTo(), swipeRight());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User creates a habit, selects the habit, clicks edit button, changes some information about
|
||||||
|
* the habit, click save button, sees changes on the main window, selects habit again,
|
||||||
|
* changes color, then deletes the habit.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEditHabit()
|
||||||
|
{
|
||||||
|
String name = addHabit();
|
||||||
|
|
||||||
|
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
||||||
|
.onChildView(withId(R.id.label))
|
||||||
|
.perform(longClick());
|
||||||
|
|
||||||
|
clickActionModeMenuItem(R.string.edit);
|
||||||
|
|
||||||
|
String modifiedName = "Modified " + new Random().nextInt(10000);
|
||||||
|
typeHabitData(modifiedName, "", "1", "1");
|
||||||
|
|
||||||
|
onView(withId(R.id.buttonSave))
|
||||||
|
.perform(click());
|
||||||
|
|
||||||
|
assertHabitExists(modifiedName);
|
||||||
|
|
||||||
|
selectHabit(modifiedName);
|
||||||
|
clickActionModeMenuItem(R.string.color_picker_default_title);
|
||||||
|
pressBack();
|
||||||
|
|
||||||
|
deleteHabit(modifiedName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User creates a habit, opens statistics page, clicks button to edit history, adds some
|
||||||
|
* checkmarks, closes dialog, sees the modified history calendar.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEditHistory()
|
||||||
|
{
|
||||||
|
String name = addHabit();
|
||||||
|
|
||||||
|
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
||||||
|
.onChildView(withId(R.id.label))
|
||||||
|
.perform(click());
|
||||||
|
|
||||||
|
openHistoryEditor();
|
||||||
|
onView(withClassName(endsWith("HabitHistoryView")))
|
||||||
|
.perform(clickAtRandomLocations(20));
|
||||||
|
|
||||||
|
pressBack();
|
||||||
|
onView(withId(R.id.historyView))
|
||||||
|
.perform(scrollTo(), swipeRight(), swipeLeft());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User opens menu, clicks settings, sees settings screen.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSettings()
|
||||||
|
{
|
||||||
|
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||||
|
onView(withText(R.string.settings)).perform(click());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User opens menu, clicks about, sees about screen.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAbout()
|
||||||
|
{
|
||||||
|
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||||
|
onView(withText(R.string.about)).perform(click());
|
||||||
|
onView(isRoot()).perform(swipeUp());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User opens menu, clicks Help, sees website.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testHelp()
|
||||||
|
{
|
||||||
|
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||||
|
onView(withText(R.string.help)).perform(click());
|
||||||
|
intended(hasAction(Intent.ACTION_VIEW));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User creates a habit, exports full backup, deletes the habit, restores backup, sees that the
|
||||||
|
* previously created habit has appeared back.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testExportImportDB()
|
||||||
|
{
|
||||||
|
String name = addHabit();
|
||||||
|
|
||||||
|
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||||
|
onView(withText(R.string.settings)).perform(click());
|
||||||
|
|
||||||
|
String date = DateHelper.getBackupDateFormat().format(DateHelper.getLocalTime());
|
||||||
|
date = date.substring(0, date.length() - 2);
|
||||||
|
|
||||||
|
onData(isPreferenceWithText("Export full backup")).perform(click());
|
||||||
|
intended(hasAction(Intent.ACTION_SEND));
|
||||||
|
|
||||||
|
deleteHabit(name);
|
||||||
|
|
||||||
|
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||||
|
onView(withText(R.string.settings)).perform(click());
|
||||||
|
onData(isPreferenceWithText("Import data")).perform(click());
|
||||||
|
|
||||||
|
onData(allOf(is(instanceOf(String.class)), startsWith("Backups")))
|
||||||
|
.perform(click());
|
||||||
|
|
||||||
|
onData(allOf(is(instanceOf(String.class)), containsString(date)))
|
||||||
|
.perform(click());
|
||||||
|
|
||||||
|
selectHabit(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User creates a habit, opens settings, clicks export as CSV, is asked what activity should
|
||||||
|
* handle the file.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testExportCSV()
|
||||||
|
{
|
||||||
|
addHabit();
|
||||||
|
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||||
|
onView(withText(R.string.settings)).perform(click());
|
||||||
|
onData(isPreferenceWithText("Export as CSV")).perform(click());
|
||||||
|
intended(hasAction(Intent.ACTION_SEND));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User opens the settings and generates a bug report.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGenerateBugReport()
|
||||||
|
{
|
||||||
|
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||||
|
onView(withText(R.string.settings)).perform(click());
|
||||||
|
onData(isPreferenceWithText("Generate bug report")).perform(click());
|
||||||
|
intended(hasAction(Intent.ACTION_SENDTO));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,18 +17,21 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits.ui;
|
||||||
|
|
||||||
|
import android.support.test.espresso.matcher.ViewMatchers;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
import static android.support.test.espresso.Espresso.onView;
|
||||||
import static android.support.test.espresso.action.ViewActions.click;
|
import static android.support.test.espresso.action.ViewActions.click;
|
||||||
import static android.support.test.espresso.action.ViewActions.scrollTo;
|
import static android.support.test.espresso.action.ViewActions.scrollTo;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
|
|
||||||
public class ShowHabitActivityActions
|
public class ShowHabitActivityActions
|
||||||
{
|
{
|
||||||
public static void openHistoryEditor()
|
public static void openHistoryEditor()
|
||||||
{
|
{
|
||||||
onView(withId(R.id.btEditHistory))
|
onView(ViewMatchers.withId(R.id.btEditHistory))
|
||||||
.perform(scrollTo(), click());
|
.perform(scrollTo(), click());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
105
app/src/androidTest/java/org/isoron/uhabits/ui/SystemHelper.java
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package org.isoron.uhabits.ui;
|
||||||
|
|
||||||
|
import android.app.KeyguardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.support.test.runner.AndroidJUnitRunner;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public final class SystemHelper extends AndroidJUnitRunner
|
||||||
|
{
|
||||||
|
private static final String ANIMATION_PERMISSION = "android.permission.SET_ANIMATION_SCALE";
|
||||||
|
private static final float DISABLED = 0.0f;
|
||||||
|
private static final float DEFAULT = 1.0f;
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private PowerManager.WakeLock wakeLock;
|
||||||
|
|
||||||
|
SystemHelper(Context context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
void acquireWakeLock()
|
||||||
|
{
|
||||||
|
PowerManager power = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||||
|
wakeLock = power.newWakeLock(PowerManager.FULL_WAKE_LOCK |
|
||||||
|
PowerManager.ACQUIRE_CAUSES_WAKEUP |
|
||||||
|
PowerManager.ON_AFTER_RELEASE, getClass().getSimpleName());
|
||||||
|
wakeLock.acquire();
|
||||||
|
}
|
||||||
|
|
||||||
|
void releaseWakeLock()
|
||||||
|
{
|
||||||
|
if(wakeLock != null)
|
||||||
|
wakeLock.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlockScreen()
|
||||||
|
{
|
||||||
|
Log.i("SystemHelper", "Trying to unlock screen");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
KeyguardManager mKeyGuardManager =
|
||||||
|
(KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
|
||||||
|
KeyguardManager.KeyguardLock mLock = mKeyGuardManager.newKeyguardLock("lock");
|
||||||
|
mLock.disableKeyguard();
|
||||||
|
Log.e("SystemHelper", "Successfully unlocked screen");
|
||||||
|
} catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.e("SystemHelper", "Could not unlock screen");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disableAllAnimations()
|
||||||
|
{
|
||||||
|
Log.i("SystemHelper", "Trying to disable animations");
|
||||||
|
int permStatus = context.checkCallingOrSelfPermission(ANIMATION_PERMISSION);
|
||||||
|
if (permStatus == PackageManager.PERMISSION_GRANTED) setSystemAnimationsScale(DISABLED);
|
||||||
|
else Log.e("SystemHelper", "Permission denied");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void enableAllAnimations()
|
||||||
|
{
|
||||||
|
int permStatus = context.checkCallingOrSelfPermission(ANIMATION_PERMISSION);
|
||||||
|
if (permStatus == PackageManager.PERMISSION_GRANTED)
|
||||||
|
{
|
||||||
|
setSystemAnimationsScale(DEFAULT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSystemAnimationsScale(float animationScale)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Class<?> windowManagerStubClazz = Class.forName("android.view.IWindowManager$Stub");
|
||||||
|
Method asInterface =
|
||||||
|
windowManagerStubClazz.getDeclaredMethod("asInterface", IBinder.class);
|
||||||
|
Class<?> serviceManagerClazz = Class.forName("android.os.ServiceManager");
|
||||||
|
Method getService = serviceManagerClazz.getDeclaredMethod("getService", String.class);
|
||||||
|
Class<?> windowManagerClazz = Class.forName("android.view.IWindowManager");
|
||||||
|
Method setAnimationScales =
|
||||||
|
windowManagerClazz.getDeclaredMethod("setAnimationScales", float[].class);
|
||||||
|
Method getAnimationScales = windowManagerClazz.getDeclaredMethod("getAnimationScales");
|
||||||
|
|
||||||
|
IBinder windowManagerBinder = (IBinder) getService.invoke(null, "window");
|
||||||
|
Object windowManagerObj = asInterface.invoke(null, windowManagerBinder);
|
||||||
|
float[] currentScales = (float[]) getAnimationScales.invoke(windowManagerObj);
|
||||||
|
for (int i = 0; i < currentScales.length; i++)
|
||||||
|
currentScales[i] = animationScale;
|
||||||
|
|
||||||
|
setAnimationScales.invoke(windowManagerObj, new Object[]{currentScales});
|
||||||
|
Log.i("SystemHelper", "All animations successfully disabled");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.e("SystemHelper", "Could not change animation scale to " + animationScale + " :'(");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
|
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.tasks.BaseTask;
|
||||||
|
import org.isoron.uhabits.tasks.ExportDBTask;
|
||||||
|
import org.isoron.uhabits.tasks.ImportDataTask;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
public class HabitFixtures
|
||||||
|
{
|
||||||
|
public static boolean NON_DAILY_HABIT_CHECKS[] = { true, false, false, true, true, true, false,
|
||||||
|
false, true, true };
|
||||||
|
|
||||||
|
public static Habit createShortHabit()
|
||||||
|
{
|
||||||
|
Habit habit = new Habit();
|
||||||
|
habit.name = "Wake up early";
|
||||||
|
habit.description = "Did you wake up before 6am?";
|
||||||
|
habit.freqNum = 2;
|
||||||
|
habit.freqDen = 3;
|
||||||
|
habit.save();
|
||||||
|
|
||||||
|
long timestamp = DateHelper.getStartOfToday();
|
||||||
|
for(boolean c : NON_DAILY_HABIT_CHECKS)
|
||||||
|
{
|
||||||
|
if(c) habit.repetitions.toggle(timestamp);
|
||||||
|
timestamp -= DateHelper.millisecondsInOneDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
return habit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Habit createEmptyHabit()
|
||||||
|
{
|
||||||
|
Habit habit = new Habit();
|
||||||
|
habit.name = "Meditate";
|
||||||
|
habit.description = "Did you meditate this morning?";
|
||||||
|
habit.color = ColorHelper.palette[3];
|
||||||
|
habit.freqNum = 1;
|
||||||
|
habit.freqDen = 1;
|
||||||
|
habit.save();
|
||||||
|
return habit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Habit createLongHabit()
|
||||||
|
{
|
||||||
|
Habit habit = createEmptyHabit();
|
||||||
|
habit.freqNum = 3;
|
||||||
|
habit.freqDen = 7;
|
||||||
|
habit.color = ColorHelper.palette[4];
|
||||||
|
habit.save();
|
||||||
|
|
||||||
|
long day = DateHelper.millisecondsInOneDay;
|
||||||
|
long today = DateHelper.getStartOfToday();
|
||||||
|
int marks[] = { 0, 1, 3, 5, 7, 8, 9, 10, 12, 14, 15, 17, 19, 20, 26, 27, 28, 50, 51, 52,
|
||||||
|
53, 54, 58, 60, 63, 65, 70, 71, 72, 73, 74, 75, 80, 81, 83, 89, 90, 91, 95,
|
||||||
|
102, 103, 108, 109, 120};
|
||||||
|
|
||||||
|
for(int mark : marks)
|
||||||
|
habit.repetitions.toggle(today - mark * day);
|
||||||
|
|
||||||
|
return habit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void generateHugeDataSet() throws Throwable
|
||||||
|
{
|
||||||
|
final int nHabits = 30;
|
||||||
|
final int nYears = 5;
|
||||||
|
|
||||||
|
DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void execute()
|
||||||
|
{
|
||||||
|
Random rand = new Random();
|
||||||
|
|
||||||
|
for(int i = 0; i < nHabits; i++)
|
||||||
|
{
|
||||||
|
Log.i("HabitFixture", String.format("Creating habit %d / %d", i, nHabits));
|
||||||
|
|
||||||
|
Habit habit = new Habit();
|
||||||
|
habit.name = String.format("Habit %d", i);
|
||||||
|
habit.save();
|
||||||
|
|
||||||
|
long today = DateHelper.getStartOfToday();
|
||||||
|
long day = DateHelper.millisecondsInOneDay;
|
||||||
|
|
||||||
|
|
||||||
|
for(int j = 0; j < 365 * nYears; j++)
|
||||||
|
{
|
||||||
|
if(rand.nextBoolean())
|
||||||
|
habit.repetitions.toggle(today - j * day);
|
||||||
|
}
|
||||||
|
|
||||||
|
habit.scores.getTodayValue();
|
||||||
|
habit.streaks.getAll(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ExportDBTask task = new ExportDBTask(null);
|
||||||
|
task.setListener(new ExportDBTask.Listener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onExportDBFinished(@Nullable String filename)
|
||||||
|
{
|
||||||
|
if(filename != null)
|
||||||
|
Log.i("HabitFixture", String.format("Huge data set exported to %s", filename));
|
||||||
|
else
|
||||||
|
Log.i("HabitFixture", "Failed to save database");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
task.execute();
|
||||||
|
|
||||||
|
BaseTask.waitForTasks(30000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadHugeDataSet(Context testContext) throws Throwable
|
||||||
|
{
|
||||||
|
File baseDir = DatabaseHelper.getFilesDir("Backups");
|
||||||
|
if(baseDir == null) fail("baseDir should not be null");
|
||||||
|
|
||||||
|
File dst = new File(String.format("%s/%s", baseDir.getPath(), "loopHuge.db"));
|
||||||
|
InputStream in = testContext.getAssets().open("fixtures/loopHuge.db");
|
||||||
|
DatabaseHelper.copy(in, dst);
|
||||||
|
|
||||||
|
ImportDataTask task = new ImportDataTask(dst, null);
|
||||||
|
task.execute();
|
||||||
|
|
||||||
|
BaseTask.waitForTasks(30000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void purgeHabits()
|
||||||
|
{
|
||||||
|
for(Habit h : Habit.getAll(true))
|
||||||
|
h.cascadeDelete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.HabitsApplication;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class HabitsApplicationTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void test_getLogcat() throws IOException
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String msg = "LOGCAT TEST";
|
||||||
|
new RuntimeException(msg).printStackTrace();
|
||||||
|
|
||||||
|
String log = HabitsApplication.getLogcat();
|
||||||
|
assertThat(log, containsString(msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.commands;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertFalse;
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class ArchiveHabitsCommandTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
private ArchiveHabitsCommand command;
|
||||||
|
private Habit habit;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
habit = HabitFixtures.createShortHabit();
|
||||||
|
command = new ArchiveHabitsCommand(Collections.singletonList(habit));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteUndoRedo()
|
||||||
|
{
|
||||||
|
assertFalse(habit.isArchived());
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
assertTrue(habit.isArchived());
|
||||||
|
|
||||||
|
command.undo();
|
||||||
|
assertFalse(habit.isArchived());
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
assertTrue(habit.isArchived());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.commands;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
||||||
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class ChangeHabitColorCommandTest extends BaseTest
|
||||||
|
{
|
||||||
|
private ChangeHabitColorCommand command;
|
||||||
|
private LinkedList<Habit> habits;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
habits = new LinkedList<>();
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; i ++)
|
||||||
|
{
|
||||||
|
Habit habit = HabitFixtures.createShortHabit();
|
||||||
|
habit.color = ColorHelper.palette[i+1];
|
||||||
|
habit.save();
|
||||||
|
habits.add(habit);
|
||||||
|
}
|
||||||
|
|
||||||
|
command = new ChangeHabitColorCommand(habits, ColorHelper.palette[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteUndoRedo()
|
||||||
|
{
|
||||||
|
checkOriginalColors();
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
checkNewColors();
|
||||||
|
|
||||||
|
command.undo();
|
||||||
|
checkOriginalColors();
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
checkNewColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkOriginalColors()
|
||||||
|
{
|
||||||
|
int k = 0;
|
||||||
|
for(Habit h : habits)
|
||||||
|
assertThat(h.color, equalTo(ColorHelper.palette[++k]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkNewColors()
|
||||||
|
{
|
||||||
|
for(Habit h : habits)
|
||||||
|
assertThat(h.color, equalTo(ColorHelper.palette[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.commands;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.commands.CreateHabitCommand;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class CreateHabitCommandTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
private CreateHabitCommand command;
|
||||||
|
private Habit model;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
model = new Habit();
|
||||||
|
model.name = "New habit";
|
||||||
|
command = new CreateHabitCommand(model);
|
||||||
|
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteUndoRedo()
|
||||||
|
{
|
||||||
|
assertTrue(Habit.getAll(true).isEmpty());
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
|
||||||
|
List<Habit> allHabits = Habit.getAll(true);
|
||||||
|
assertThat(allHabits.size(), equalTo(1));
|
||||||
|
|
||||||
|
Habit habit = allHabits.get(0);
|
||||||
|
Long id = habit.getId();
|
||||||
|
assertThat(habit.name, equalTo(model.name));
|
||||||
|
|
||||||
|
command.undo();
|
||||||
|
assertTrue(Habit.getAll(true).isEmpty());
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
allHabits = Habit.getAll(true);
|
||||||
|
assertThat(allHabits.size(), equalTo(1));
|
||||||
|
|
||||||
|
habit = allHabits.get(0);
|
||||||
|
Long newId = habit.getId();
|
||||||
|
assertThat(id, equalTo(newId));
|
||||||
|
assertThat(habit.name, equalTo(model.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.commands;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.commands.DeleteHabitsCommand;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ExpectedException;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class DeleteHabitsCommandTest extends BaseTest
|
||||||
|
{
|
||||||
|
private DeleteHabitsCommand command;
|
||||||
|
private LinkedList<Habit> habits;
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ExpectedException thrown = ExpectedException.none();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
habits = new LinkedList<>();
|
||||||
|
|
||||||
|
// Habits that shuold be deleted
|
||||||
|
for(int i = 0; i < 3; i ++)
|
||||||
|
{
|
||||||
|
Habit habit = HabitFixtures.createShortHabit();
|
||||||
|
habits.add(habit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra habit that should not be deleted
|
||||||
|
Habit extraHabit = HabitFixtures.createShortHabit();
|
||||||
|
extraHabit.name = "extra";
|
||||||
|
extraHabit.save();
|
||||||
|
|
||||||
|
command = new DeleteHabitsCommand(habits);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteUndoRedo()
|
||||||
|
{
|
||||||
|
assertThat(Habit.getAll(true).size(), equalTo(4));
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
assertThat(Habit.getAll(true).size(), equalTo(1));
|
||||||
|
assertThat(Habit.getAll(true).get(0).name, equalTo("extra"));
|
||||||
|
|
||||||
|
thrown.expect(UnsupportedOperationException.class);
|
||||||
|
command.undo();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.commands;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.commands.EditHabitCommand;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class EditHabitCommandTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
private EditHabitCommand command;
|
||||||
|
private Habit habit;
|
||||||
|
private Habit modified;
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
habit = HabitFixtures.createShortHabit();
|
||||||
|
habit.name = "original";
|
||||||
|
habit.freqDen = 1;
|
||||||
|
habit.freqNum = 1;
|
||||||
|
habit.save();
|
||||||
|
|
||||||
|
id = habit.getId();
|
||||||
|
|
||||||
|
modified = new Habit(habit);
|
||||||
|
modified.name = "modified";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteUndoRedo()
|
||||||
|
{
|
||||||
|
command = new EditHabitCommand(habit, modified);
|
||||||
|
|
||||||
|
int originalScore = habit.scores.getTodayValue();
|
||||||
|
assertThat(habit.name, equalTo("original"));
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
refreshHabit();
|
||||||
|
assertThat(habit.name, equalTo("modified"));
|
||||||
|
assertThat(habit.scores.getTodayValue(), equalTo(originalScore));
|
||||||
|
|
||||||
|
command.undo();
|
||||||
|
refreshHabit();
|
||||||
|
assertThat(habit.name, equalTo("original"));
|
||||||
|
assertThat(habit.scores.getTodayValue(), equalTo(originalScore));
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
refreshHabit();
|
||||||
|
assertThat(habit.name, equalTo("modified"));
|
||||||
|
assertThat(habit.scores.getTodayValue(), equalTo(originalScore));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteUndoRedo_withModifiedInterval()
|
||||||
|
{
|
||||||
|
modified.freqNum = 1;
|
||||||
|
modified.freqDen = 7;
|
||||||
|
command = new EditHabitCommand(habit, modified);
|
||||||
|
|
||||||
|
int originalScore = habit.scores.getTodayValue();
|
||||||
|
assertThat(habit.name, equalTo("original"));
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
refreshHabit();
|
||||||
|
assertThat(habit.name, equalTo("modified"));
|
||||||
|
assertThat(habit.scores.getTodayValue(), greaterThan(originalScore));
|
||||||
|
|
||||||
|
command.undo();
|
||||||
|
refreshHabit();
|
||||||
|
assertThat(habit.name, equalTo("original"));
|
||||||
|
assertThat(habit.scores.getTodayValue(), equalTo(originalScore));
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
refreshHabit();
|
||||||
|
assertThat(habit.name, equalTo("modified"));
|
||||||
|
assertThat(habit.scores.getTodayValue(), greaterThan(originalScore));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshHabit()
|
||||||
|
{
|
||||||
|
habit = Habit.get(id);
|
||||||
|
assertTrue(habit != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.commands;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertFalse;
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class ToggleRepetitionCommandTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
private ToggleRepetitionCommand command;
|
||||||
|
private Habit habit;
|
||||||
|
private long today;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
habit = HabitFixtures.createShortHabit();
|
||||||
|
|
||||||
|
today = DateHelper.getStartOfToday();
|
||||||
|
command = new ToggleRepetitionCommand(habit, today);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteUndoRedo()
|
||||||
|
{
|
||||||
|
assertTrue(habit.repetitions.contains(today));
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
assertFalse(habit.repetitions.contains(today));
|
||||||
|
|
||||||
|
command.undo();
|
||||||
|
assertTrue(habit.repetitions.contains(today));
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
assertFalse(habit.repetitions.contains(today));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.commands;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.commands.UnarchiveHabitsCommand;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertFalse;
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class UnarchiveHabitsCommandTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
private UnarchiveHabitsCommand command;
|
||||||
|
private Habit habit;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
habit = HabitFixtures.createShortHabit();
|
||||||
|
Habit.archive(Collections.singletonList(habit));
|
||||||
|
|
||||||
|
command = new UnarchiveHabitsCommand(Collections.singletonList(habit));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteUndoRedo()
|
||||||
|
{
|
||||||
|
assertTrue(habit.isArchived());
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
assertFalse(habit.isArchived());
|
||||||
|
|
||||||
|
command.undo();
|
||||||
|
assertTrue(habit.isArchived());
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
assertFalse(habit.isArchived());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.io;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||||
|
import org.isoron.uhabits.io.HabitsCSVExporter;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class HabitsCSVExporterTest extends BaseTest
|
||||||
|
{
|
||||||
|
private File baseDir;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
HabitFixtures.createShortHabit();
|
||||||
|
HabitFixtures.createEmptyHabit();
|
||||||
|
|
||||||
|
Context targetContext = InstrumentationRegistry.getTargetContext();
|
||||||
|
baseDir = targetContext.getCacheDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unzip(File file) throws IOException
|
||||||
|
{
|
||||||
|
ZipFile zip = new ZipFile(file);
|
||||||
|
Enumeration<? extends ZipEntry> e = zip.entries();
|
||||||
|
|
||||||
|
while(e.hasMoreElements())
|
||||||
|
{
|
||||||
|
ZipEntry entry = e.nextElement();
|
||||||
|
InputStream stream = zip.getInputStream(entry);
|
||||||
|
|
||||||
|
String outputFilename = String.format("%s/%s", baseDir.getAbsolutePath(),
|
||||||
|
entry.getName());
|
||||||
|
File outputFile = new File(outputFilename);
|
||||||
|
|
||||||
|
File parent = outputFile.getParentFile();
|
||||||
|
if(parent != null) parent.mkdirs();
|
||||||
|
|
||||||
|
DatabaseHelper.copy(stream, outputFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
zip.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExportCSV() throws IOException
|
||||||
|
{
|
||||||
|
List<Habit> habits = Habit.getAll(true);
|
||||||
|
|
||||||
|
HabitsCSVExporter exporter = new HabitsCSVExporter(habits, baseDir);
|
||||||
|
String filename = exporter.writeArchive();
|
||||||
|
assertAbsolutePathExists(filename);
|
||||||
|
|
||||||
|
File archive = new File(filename);
|
||||||
|
unzip(archive);
|
||||||
|
|
||||||
|
assertPathExists("Habits.csv");
|
||||||
|
assertPathExists("001 Wake up early");
|
||||||
|
assertPathExists("001 Wake up early/Checkmarks.csv");
|
||||||
|
assertPathExists("001 Wake up early/Scores.csv");
|
||||||
|
assertPathExists("002 Meditate/Checkmarks.csv");
|
||||||
|
assertPathExists("002 Meditate/Scores.csv");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertPathExists(String s)
|
||||||
|
{
|
||||||
|
assertAbsolutePathExists(String.format("%s/%s", baseDir.getAbsolutePath(), s));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAbsolutePathExists(String s)
|
||||||
|
{
|
||||||
|
File file = new File(s);
|
||||||
|
assertTrue(String.format("File %s should exist", file.getAbsolutePath()), file.exists());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.io;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.io.GenericImporter;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class ImportTest extends BaseTest
|
||||||
|
{
|
||||||
|
private File baseDir;
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
DateHelper.setFixedLocalTime(null);
|
||||||
|
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
context = InstrumentationRegistry.getInstrumentation().getContext();
|
||||||
|
baseDir = DatabaseHelper.getFilesDir("Backups");
|
||||||
|
if(baseDir == null) fail("baseDir should not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyAssetToFile(String assetPath, File dst) throws IOException
|
||||||
|
{
|
||||||
|
InputStream in = context.getAssets().open(assetPath);
|
||||||
|
DatabaseHelper.copy(in, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void importFromFile(String assetFilename) throws IOException
|
||||||
|
{
|
||||||
|
File file = new File(String.format("%s/%s", baseDir.getPath(), assetFilename));
|
||||||
|
copyAssetToFile(assetFilename, file);
|
||||||
|
assertTrue(file.exists());
|
||||||
|
assertTrue(file.canRead());
|
||||||
|
|
||||||
|
GenericImporter importer = new GenericImporter();
|
||||||
|
assertThat(importer.canHandle(file), is(true));
|
||||||
|
|
||||||
|
importer.importHabitsFromFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsRepetition(Habit h, int year, int month, int day)
|
||||||
|
{
|
||||||
|
GregorianCalendar date = DateHelper.getStartOfTodayCalendar();
|
||||||
|
date.set(year, month - 1, day);
|
||||||
|
return h.repetitions.contains(date.getTimeInMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTickmateDB() throws IOException
|
||||||
|
{
|
||||||
|
importFromFile("tickmate.db");
|
||||||
|
|
||||||
|
List<Habit> habits = Habit.getAll(true);
|
||||||
|
assertThat(habits.size(), equalTo(3));
|
||||||
|
|
||||||
|
Habit h = habits.get(0);
|
||||||
|
assertThat(h.name, equalTo("Vegan"));
|
||||||
|
assertTrue(containsRepetition(h, 2016, 1, 24));
|
||||||
|
assertTrue(containsRepetition(h, 2016, 2, 5));
|
||||||
|
assertTrue(containsRepetition(h, 2016, 3, 18));
|
||||||
|
assertFalse(containsRepetition(h, 2016, 3, 14));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRewireDB() throws IOException
|
||||||
|
{
|
||||||
|
importFromFile("rewire.db");
|
||||||
|
|
||||||
|
List<Habit> habits = Habit.getAll(true);
|
||||||
|
assertThat(habits.size(), equalTo(3));
|
||||||
|
|
||||||
|
Habit habit = habits.get(0);
|
||||||
|
assertThat(habit.name, equalTo("Wake up early"));
|
||||||
|
assertThat(habit.freqNum, equalTo(3));
|
||||||
|
assertThat(habit.freqDen, equalTo(7));
|
||||||
|
assertFalse(habit.hasReminder());
|
||||||
|
assertFalse(containsRepetition(habit, 2015, 12, 31));
|
||||||
|
assertTrue(containsRepetition(habit, 2016, 1, 18));
|
||||||
|
assertTrue(containsRepetition(habit, 2016, 1, 28));
|
||||||
|
assertFalse(containsRepetition(habit, 2016, 3, 10));
|
||||||
|
|
||||||
|
habit = habits.get(1);
|
||||||
|
assertThat(habit.name, equalTo("brush teeth"));
|
||||||
|
assertThat(habit.freqNum, equalTo(3));
|
||||||
|
assertThat(habit.freqDen, equalTo(7));
|
||||||
|
assertThat(habit.reminderHour, equalTo(8));
|
||||||
|
assertThat(habit.reminderMin, equalTo(0));
|
||||||
|
boolean[] reminderDays = {false, true, true, true, true, true, false};
|
||||||
|
assertThat(habit.reminderDays, equalTo(DateHelper.packWeekdayList(reminderDays)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHabitBullCSV() throws IOException
|
||||||
|
{
|
||||||
|
importFromFile("habitbull.csv");
|
||||||
|
|
||||||
|
List<Habit> habits = Habit.getAll(true);
|
||||||
|
assertThat(habits.size(), equalTo(4));
|
||||||
|
|
||||||
|
Habit habit = habits.get(0);
|
||||||
|
assertThat(habit.name, equalTo("Breed dragons"));
|
||||||
|
assertThat(habit.description, equalTo("with love and fire"));
|
||||||
|
assertThat(habit.freqNum, equalTo(1));
|
||||||
|
assertThat(habit.freqDen, equalTo(1));
|
||||||
|
assertTrue(containsRepetition(habit, 2016, 3, 18));
|
||||||
|
assertTrue(containsRepetition(habit, 2016, 3, 19));
|
||||||
|
assertFalse(containsRepetition(habit, 2016, 3, 20));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoopDB() throws IOException
|
||||||
|
{
|
||||||
|
importFromFile("loop.db");
|
||||||
|
|
||||||
|
List<Habit> habits = Habit.getAll(true);
|
||||||
|
assertThat(habits.size(), equalTo(9));
|
||||||
|
|
||||||
|
Habit habit = habits.get(0);
|
||||||
|
assertThat(habit.name, equalTo("Wake up early"));
|
||||||
|
assertThat(habit.freqNum, equalTo(3));
|
||||||
|
assertThat(habit.freqDen, equalTo(7));
|
||||||
|
assertTrue(containsRepetition(habit, 2016, 3, 14));
|
||||||
|
assertTrue(containsRepetition(habit, 2016, 3, 16));
|
||||||
|
assertFalse(containsRepetition(habit, 2016, 3, 17));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.models;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.isoron.uhabits.models.Checkmark.CHECKED_EXPLICITLY;
|
||||||
|
import static org.isoron.uhabits.models.Checkmark.CHECKED_IMPLICITLY;
|
||||||
|
import static org.isoron.uhabits.models.Checkmark.UNCHECKED;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class CheckmarkListTest extends BaseTest
|
||||||
|
{
|
||||||
|
Habit nonDailyHabit;
|
||||||
|
private Habit emptyHabit;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
nonDailyHabit = HabitFixtures.createShortHabit();
|
||||||
|
emptyHabit = HabitFixtures.createEmptyHabit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown()
|
||||||
|
{
|
||||||
|
DateHelper.setFixedLocalTime(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getAllValues_withNonDailyHabit()
|
||||||
|
{
|
||||||
|
int[] expectedValues = { CHECKED_EXPLICITLY, UNCHECKED, CHECKED_IMPLICITLY,
|
||||||
|
CHECKED_EXPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY, UNCHECKED,
|
||||||
|
CHECKED_IMPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY };
|
||||||
|
|
||||||
|
int[] actualValues = nonDailyHabit.checkmarks.getAllValues();
|
||||||
|
|
||||||
|
assertThat(actualValues, equalTo(expectedValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getAllValues_withEmptyHabit()
|
||||||
|
{
|
||||||
|
int[] expectedValues = new int[0];
|
||||||
|
int[] actualValues = emptyHabit.checkmarks.getAllValues();
|
||||||
|
|
||||||
|
assertThat(actualValues, equalTo(expectedValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getAllValues_moveForwardInTime()
|
||||||
|
{
|
||||||
|
travelInTime(3);
|
||||||
|
|
||||||
|
int[] expectedValues = { UNCHECKED, UNCHECKED, UNCHECKED, CHECKED_EXPLICITLY, UNCHECKED,
|
||||||
|
CHECKED_IMPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY,
|
||||||
|
UNCHECKED, CHECKED_IMPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY };
|
||||||
|
|
||||||
|
int[] actualValues = nonDailyHabit.checkmarks.getAllValues();
|
||||||
|
|
||||||
|
assertThat(actualValues, equalTo(expectedValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getAllValues_moveBackwardsInTime()
|
||||||
|
{
|
||||||
|
travelInTime(-3);
|
||||||
|
|
||||||
|
int[] expectedValues = { CHECKED_EXPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY,
|
||||||
|
UNCHECKED, CHECKED_IMPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY };
|
||||||
|
|
||||||
|
int[] actualValues = nonDailyHabit.checkmarks.getAllValues();
|
||||||
|
|
||||||
|
assertThat(actualValues, equalTo(expectedValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getValues_withInvalidInterval()
|
||||||
|
{
|
||||||
|
int values[] = nonDailyHabit.checkmarks.getValues(100L, -100L);
|
||||||
|
assertThat(values, equalTo(new int[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getValues_withValidInterval()
|
||||||
|
{
|
||||||
|
long from = DateHelper.getStartOfToday() - 15 * DateHelper.millisecondsInOneDay;
|
||||||
|
long to = DateHelper.getStartOfToday() - 5 * DateHelper.millisecondsInOneDay;
|
||||||
|
|
||||||
|
int[] expectedValues = { CHECKED_EXPLICITLY, UNCHECKED, CHECKED_IMPLICITLY,
|
||||||
|
CHECKED_EXPLICITLY, CHECKED_EXPLICITLY, UNCHECKED, UNCHECKED, UNCHECKED, UNCHECKED,
|
||||||
|
UNCHECKED, UNCHECKED };
|
||||||
|
|
||||||
|
int[] actualValues = nonDailyHabit.checkmarks.getValues(from, to);
|
||||||
|
|
||||||
|
assertThat(actualValues, equalTo(expectedValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getTodayValue()
|
||||||
|
{
|
||||||
|
travelInTime(-1);
|
||||||
|
assertThat(nonDailyHabit.checkmarks.getTodayValue(), equalTo(UNCHECKED));
|
||||||
|
|
||||||
|
travelInTime(0);
|
||||||
|
assertThat(nonDailyHabit.checkmarks.getTodayValue(), equalTo(CHECKED_EXPLICITLY));
|
||||||
|
|
||||||
|
travelInTime(1);
|
||||||
|
assertThat(nonDailyHabit.checkmarks.getTodayValue(), equalTo(UNCHECKED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_writeCSV() throws IOException
|
||||||
|
{
|
||||||
|
String expectedCSV =
|
||||||
|
"2015-01-16,2\n" +
|
||||||
|
"2015-01-17,2\n" +
|
||||||
|
"2015-01-18,1\n" +
|
||||||
|
"2015-01-19,0\n" +
|
||||||
|
"2015-01-20,2\n" +
|
||||||
|
"2015-01-21,2\n" +
|
||||||
|
"2015-01-22,2\n" +
|
||||||
|
"2015-01-23,1\n" +
|
||||||
|
"2015-01-24,0\n" +
|
||||||
|
"2015-01-25,2\n";
|
||||||
|
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
nonDailyHabit.checkmarks.writeCSV(writer);
|
||||||
|
|
||||||
|
assertThat(writer.toString(), equalTo(expectedCSV));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void travelInTime(int days)
|
||||||
|
{
|
||||||
|
DateHelper.setFixedLocalTime(FIXED_LOCAL_TIME +
|
||||||
|
days * DateHelper.millisecondsInOneDay);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,379 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.models;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.hamcrest.MatcherAssert;
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
import static org.hamcrest.core.IsNot.not;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class HabitTest extends BaseTest
|
||||||
|
{
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructor_default()
|
||||||
|
{
|
||||||
|
Habit habit = new Habit();
|
||||||
|
assertThat(habit.archived, is(0));
|
||||||
|
assertThat(habit.highlight, is(0));
|
||||||
|
|
||||||
|
assertThat(habit.reminderHour, is(nullValue()));
|
||||||
|
assertThat(habit.reminderMin, is(nullValue()));
|
||||||
|
|
||||||
|
assertThat(habit.reminderDays, is(not(nullValue())));
|
||||||
|
assertThat(habit.streaks, is(not(nullValue())));
|
||||||
|
assertThat(habit.scores, is(not(nullValue())));
|
||||||
|
assertThat(habit.repetitions, is(not(nullValue())));
|
||||||
|
assertThat(habit.checkmarks, is(not(nullValue())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructor_habit()
|
||||||
|
{
|
||||||
|
Habit model = new Habit();
|
||||||
|
model.archived = 1;
|
||||||
|
model.highlight = 1;
|
||||||
|
model.color = Color.BLACK;
|
||||||
|
model.freqNum = 10;
|
||||||
|
model.freqDen = 20;
|
||||||
|
model.reminderDays = 1;
|
||||||
|
model.reminderHour = 8;
|
||||||
|
model.reminderMin = 30;
|
||||||
|
model.position = 0;
|
||||||
|
|
||||||
|
Habit habit = new Habit(model);
|
||||||
|
assertThat(habit.archived, is(model.archived));
|
||||||
|
assertThat(habit.highlight, is(model.highlight));
|
||||||
|
assertThat(habit.color, is(model.color));
|
||||||
|
assertThat(habit.freqNum, is(model.freqNum));
|
||||||
|
assertThat(habit.freqDen, is(model.freqDen));
|
||||||
|
assertThat(habit.reminderDays, is(model.reminderDays));
|
||||||
|
assertThat(habit.reminderHour, is(model.reminderHour));
|
||||||
|
assertThat(habit.reminderMin, is(model.reminderMin));
|
||||||
|
assertThat(habit.position, is(model.position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_get_withValidId()
|
||||||
|
{
|
||||||
|
Habit habit = new Habit();
|
||||||
|
habit.save();
|
||||||
|
|
||||||
|
Habit habit2 = Habit.get(habit.getId());
|
||||||
|
assertThat(habit, equalTo(habit2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_get_withInvalidId()
|
||||||
|
{
|
||||||
|
Habit habit = Habit.get(123456L);
|
||||||
|
assertThat(habit, is(nullValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getAll_withoutArchived()
|
||||||
|
{
|
||||||
|
List<Habit> habits = new LinkedList<>();
|
||||||
|
List<Habit> habitsWithArchived = new LinkedList<>();
|
||||||
|
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
Habit h = new Habit();
|
||||||
|
|
||||||
|
if(i % 2 == 0)
|
||||||
|
h.archived = 1;
|
||||||
|
else
|
||||||
|
habits.add(h);
|
||||||
|
|
||||||
|
habitsWithArchived.add(h);
|
||||||
|
h.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(habits, equalTo(Habit.getAll(false)));
|
||||||
|
assertThat(habitsWithArchived, equalTo(Habit.getAll(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getByPosition()
|
||||||
|
{
|
||||||
|
List<Habit> habits = new LinkedList<>();
|
||||||
|
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
Habit h = new Habit();
|
||||||
|
h.save();
|
||||||
|
habits.add(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
Habit h = Habit.getByPosition(i);
|
||||||
|
if(h == null) fail();
|
||||||
|
assertThat(h, equalTo(habits.get(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_count()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
Habit h = new Habit();
|
||||||
|
if(i % 2 == 0) h.archived = 1;
|
||||||
|
h.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(Habit.count(), equalTo(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_countWithArchived()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
Habit h = new Habit();
|
||||||
|
if(i % 2 == 0) h.archived = 1;
|
||||||
|
h.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(Habit.countWithArchived(), equalTo(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_updateId()
|
||||||
|
{
|
||||||
|
Habit habit = new Habit();
|
||||||
|
habit.name = "Hello World";
|
||||||
|
habit.save();
|
||||||
|
|
||||||
|
Long oldId = habit.getId();
|
||||||
|
Long newId = 123456L;
|
||||||
|
Habit.updateId(oldId, newId);
|
||||||
|
|
||||||
|
Habit newHabit = Habit.get(newId);
|
||||||
|
if(newHabit == null) fail();
|
||||||
|
assertThat(newHabit, is(not(nullValue())));
|
||||||
|
assertThat(newHabit.name, equalTo(habit.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_reorder()
|
||||||
|
{
|
||||||
|
List<Long> ids = new LinkedList<>();
|
||||||
|
|
||||||
|
int n = 10;
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
Habit h = new Habit();
|
||||||
|
h.save();
|
||||||
|
ids.add(h.getId());
|
||||||
|
assertThat(h.position, is(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
int operations[][] = {
|
||||||
|
{5, 2},
|
||||||
|
{3, 7},
|
||||||
|
{4, 4},
|
||||||
|
{3, 2}
|
||||||
|
};
|
||||||
|
|
||||||
|
int expectedPosition[][] = {
|
||||||
|
{0, 1, 3, 4, 5, 2, 6, 7, 8, 9},
|
||||||
|
{0, 1, 7, 3, 4, 2, 5, 6, 8, 9},
|
||||||
|
{0, 1, 7, 3, 4, 2, 5, 6, 8, 9},
|
||||||
|
{0, 1, 7, 2, 4, 3, 5, 6, 8, 9},
|
||||||
|
};
|
||||||
|
|
||||||
|
for(int i = 0; i < operations.length; i++)
|
||||||
|
{
|
||||||
|
int from = operations[i][0];
|
||||||
|
int to = operations[i][1];
|
||||||
|
|
||||||
|
Habit fromHabit = Habit.getByPosition(from);
|
||||||
|
Habit toHabit = Habit.getByPosition(to);
|
||||||
|
Habit.reorder(fromHabit, toHabit);
|
||||||
|
|
||||||
|
int actualPositions[] = new int[n];
|
||||||
|
|
||||||
|
for (int j = 0; j < n; j++)
|
||||||
|
{
|
||||||
|
Habit h = Habit.get(ids.get(j));
|
||||||
|
if (h == null) fail();
|
||||||
|
actualPositions[j] = h.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(actualPositions, equalTo(expectedPosition[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_rebuildOrder()
|
||||||
|
{
|
||||||
|
List<Long> ids = new LinkedList<>();
|
||||||
|
int originalPositions[] = { 0, 1, 1, 4, 6, 8, 10, 10, 13};
|
||||||
|
|
||||||
|
for (int p : originalPositions)
|
||||||
|
{
|
||||||
|
Habit h = new Habit();
|
||||||
|
h.position = p;
|
||||||
|
h.save();
|
||||||
|
ids.add(h.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
Habit.rebuildOrder();
|
||||||
|
|
||||||
|
for (int i = 0; i < originalPositions.length; i++)
|
||||||
|
{
|
||||||
|
Habit h = Habit.get(ids.get(i));
|
||||||
|
if(h == null) fail();
|
||||||
|
assertThat(h.position, is(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getHabitsWithReminder()
|
||||||
|
{
|
||||||
|
List<Habit> habitsWithReminder = new LinkedList<>();
|
||||||
|
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
Habit habit = new Habit();
|
||||||
|
if(i % 2 == 0)
|
||||||
|
{
|
||||||
|
habit.reminderDays = DateHelper.ALL_WEEK_DAYS;
|
||||||
|
habit.reminderHour = 8;
|
||||||
|
habit.reminderMin = 30;
|
||||||
|
habitsWithReminder.add(habit);
|
||||||
|
}
|
||||||
|
habit.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(habitsWithReminder, equalTo(Habit.getHabitsWithReminder()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_archive_unarchive()
|
||||||
|
{
|
||||||
|
List<Habit> allHabits = new LinkedList<>();
|
||||||
|
List<Habit> archivedHabits = new LinkedList<>();
|
||||||
|
List<Habit> unarchivedHabits = new LinkedList<>();
|
||||||
|
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
Habit habit = new Habit();
|
||||||
|
habit.save();
|
||||||
|
allHabits.add(habit);
|
||||||
|
|
||||||
|
if(i % 2 == 0)
|
||||||
|
archivedHabits.add(habit);
|
||||||
|
else
|
||||||
|
unarchivedHabits.add(habit);
|
||||||
|
}
|
||||||
|
|
||||||
|
Habit.archive(archivedHabits);
|
||||||
|
assertThat(Habit.getAll(false), equalTo(unarchivedHabits));
|
||||||
|
assertThat(Habit.getAll(true), equalTo(allHabits));
|
||||||
|
|
||||||
|
Habit.unarchive(archivedHabits);
|
||||||
|
assertThat(Habit.getAll(false), equalTo(allHabits));
|
||||||
|
assertThat(Habit.getAll(true), equalTo(allHabits));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_setColor()
|
||||||
|
{
|
||||||
|
List<Habit> habits = new LinkedList<>();
|
||||||
|
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
Habit habit = new Habit();
|
||||||
|
habit.color = i;
|
||||||
|
habit.save();
|
||||||
|
habits.add(habit);
|
||||||
|
}
|
||||||
|
|
||||||
|
int newColor = 100;
|
||||||
|
Habit.setColor(habits, newColor);
|
||||||
|
|
||||||
|
for(Habit h : habits)
|
||||||
|
assertThat(h.color, equalTo(newColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_hasReminder_clearReminder()
|
||||||
|
{
|
||||||
|
Habit h = new Habit();
|
||||||
|
assertThat(h.hasReminder(), is(false));
|
||||||
|
|
||||||
|
h.reminderDays = DateHelper.ALL_WEEK_DAYS;
|
||||||
|
h.reminderHour = 8;
|
||||||
|
h.reminderMin = 30;
|
||||||
|
assertThat(h.hasReminder(), is(true));
|
||||||
|
|
||||||
|
h.clearReminder();
|
||||||
|
assertThat(h.hasReminder(), is(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_writeCSV() throws IOException
|
||||||
|
{
|
||||||
|
HabitFixtures.createEmptyHabit();
|
||||||
|
HabitFixtures.createShortHabit();
|
||||||
|
|
||||||
|
String expectedCSV =
|
||||||
|
"Name,Description,NumRepetitions,Interval,Color\n" +
|
||||||
|
"Meditate,Did you meditate this morning?,1,1,#AFB42B\n" +
|
||||||
|
"Wake up early,Did you wake up before 6am?,2,3,#00897B\n";
|
||||||
|
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
Habit.writeCSV(Habit.getAll(true), writer);
|
||||||
|
|
||||||
|
MatcherAssert.assertThat(writer.toString(), equalTo(expectedCSV));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.models;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.models.Repetition;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertFalse;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class RepetitionListTest extends BaseTest
|
||||||
|
{
|
||||||
|
private Habit habit;
|
||||||
|
private Habit emptyHabit;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
habit = HabitFixtures.createShortHabit();
|
||||||
|
emptyHabit = HabitFixtures.createEmptyHabit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown()
|
||||||
|
{
|
||||||
|
DateHelper.setFixedLocalTime(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_contains()
|
||||||
|
{
|
||||||
|
long current = DateHelper.getStartOfToday();
|
||||||
|
|
||||||
|
for(boolean b : HabitFixtures.NON_DAILY_HABIT_CHECKS)
|
||||||
|
{
|
||||||
|
assertThat(habit.repetitions.contains(current), equalTo(b));
|
||||||
|
current -= DateHelper.millisecondsInOneDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
assertThat(habit.repetitions.contains(current), equalTo(false));
|
||||||
|
current -= DateHelper.millisecondsInOneDay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_delete()
|
||||||
|
{
|
||||||
|
long timestamp = DateHelper.getStartOfToday();
|
||||||
|
assertThat(habit.repetitions.contains(timestamp), equalTo(true));
|
||||||
|
|
||||||
|
habit.repetitions.delete(timestamp);
|
||||||
|
assertThat(habit.repetitions.contains(timestamp), equalTo(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_toggle()
|
||||||
|
{
|
||||||
|
long timestamp = DateHelper.getStartOfToday();
|
||||||
|
assertThat(habit.repetitions.contains(timestamp), equalTo(true));
|
||||||
|
|
||||||
|
habit.repetitions.toggle(timestamp);
|
||||||
|
assertThat(habit.repetitions.contains(timestamp), equalTo(false));
|
||||||
|
|
||||||
|
habit.repetitions.toggle(timestamp);
|
||||||
|
assertThat(habit.repetitions.contains(timestamp), equalTo(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getWeekDayFrequency()
|
||||||
|
{
|
||||||
|
Random random = new Random();
|
||||||
|
Integer weekdayCount[][] = new Integer[12][7];
|
||||||
|
Integer monthCount[] = new Integer[12];
|
||||||
|
|
||||||
|
Arrays.fill(monthCount, 0);
|
||||||
|
for(Integer row[] : weekdayCount)
|
||||||
|
Arrays.fill(row, 0);
|
||||||
|
|
||||||
|
GregorianCalendar day = DateHelper.getStartOfTodayCalendar();
|
||||||
|
|
||||||
|
// Sets the current date to the end of November
|
||||||
|
day.set(2015, 10, 30);
|
||||||
|
DateHelper.setFixedLocalTime(day.getTimeInMillis());
|
||||||
|
|
||||||
|
// Add repetitions randomly from January to December
|
||||||
|
// Leaves the month of March empty, to check that it returns null
|
||||||
|
day.set(2015, 0, 1);
|
||||||
|
for(int i = 0; i < 365; i ++)
|
||||||
|
{
|
||||||
|
if(random.nextBoolean())
|
||||||
|
{
|
||||||
|
int month = day.get(Calendar.MONTH);
|
||||||
|
int week = day.get(Calendar.DAY_OF_WEEK) % 7;
|
||||||
|
|
||||||
|
if(month != 2)
|
||||||
|
{
|
||||||
|
if (month <= 10)
|
||||||
|
{
|
||||||
|
weekdayCount[month][week]++;
|
||||||
|
monthCount[month]++;
|
||||||
|
}
|
||||||
|
emptyHabit.repetitions.toggle(day.getTimeInMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
day.add(Calendar.DAY_OF_YEAR, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<Long, Integer[]> freq = emptyHabit.repetitions.getWeekdayFrequency();
|
||||||
|
|
||||||
|
// Repetitions until November should be counted correctly
|
||||||
|
for(int month = 0; month < 11; month++)
|
||||||
|
{
|
||||||
|
day.set(2015, month, 1);
|
||||||
|
Integer actualCount[] = freq.get(day.getTimeInMillis());
|
||||||
|
if(monthCount[month] == 0)
|
||||||
|
assertThat(actualCount, equalTo(null));
|
||||||
|
else
|
||||||
|
assertThat(actualCount, equalTo(weekdayCount[month]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repetitions in December should be discarded
|
||||||
|
day.set(2015, 11, 1);
|
||||||
|
assertThat(freq.get(day.getTimeInMillis()), equalTo(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_count()
|
||||||
|
{
|
||||||
|
long to = DateHelper.getStartOfToday();
|
||||||
|
long from = to - 9 * DateHelper.millisecondsInOneDay;
|
||||||
|
assertThat(habit.repetitions.count(from, to), equalTo(6));
|
||||||
|
|
||||||
|
to = DateHelper.getStartOfToday() - DateHelper.millisecondsInOneDay;
|
||||||
|
from = to - 5 * DateHelper.millisecondsInOneDay;
|
||||||
|
assertThat(habit.repetitions.count(from, to), equalTo(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getOldest()
|
||||||
|
{
|
||||||
|
long expectedOldestTimestamp = DateHelper.getStartOfToday() - 9 * DateHelper.millisecondsInOneDay;
|
||||||
|
|
||||||
|
assertThat(habit.repetitions.getOldestTimestamp(), equalTo(expectedOldestTimestamp));
|
||||||
|
|
||||||
|
Repetition oldest = habit.repetitions.getOldest();
|
||||||
|
assertFalse(oldest == null);
|
||||||
|
assertThat(oldest.timestamp, equalTo(expectedOldestTimestamp));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.models;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.models.Score;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class ScoreListTest extends BaseTest
|
||||||
|
{
|
||||||
|
private Habit habit;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
habit = HabitFixtures.createEmptyHabit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown()
|
||||||
|
{
|
||||||
|
DateHelper.setFixedLocalTime(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_invalidateNewerThan()
|
||||||
|
{
|
||||||
|
assertThat(habit.scores.getTodayValue(), equalTo(0));
|
||||||
|
|
||||||
|
toggleRepetitions(0, 2);
|
||||||
|
assertThat(habit.scores.getTodayValue(), equalTo(1948077));
|
||||||
|
|
||||||
|
habit.freqNum = 1;
|
||||||
|
habit.freqDen = 2;
|
||||||
|
habit.scores.invalidateNewerThan(0);
|
||||||
|
|
||||||
|
assertThat(habit.scores.getTodayValue(), equalTo(1974654));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getTodayStarValue()
|
||||||
|
{
|
||||||
|
assertThat(habit.scores.getTodayStarStatus(), equalTo(Score.EMPTY_STAR));
|
||||||
|
|
||||||
|
int k = 0;
|
||||||
|
while(habit.scores.getTodayValue() < Score.HALF_STAR_CUTOFF) toggleRepetitions(k, ++k);
|
||||||
|
assertThat(habit.scores.getTodayStarStatus(), equalTo(Score.HALF_STAR));
|
||||||
|
|
||||||
|
while(habit.scores.getTodayValue() < Score.FULL_STAR_CUTOFF) toggleRepetitions(k, ++k);
|
||||||
|
assertThat(habit.scores.getTodayStarStatus(), equalTo(Score.FULL_STAR));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getTodayValue()
|
||||||
|
{
|
||||||
|
toggleRepetitions(0, 20);
|
||||||
|
assertThat(habit.scores.getTodayValue(), equalTo(12629351));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getValue()
|
||||||
|
{
|
||||||
|
toggleRepetitions(0, 20);
|
||||||
|
|
||||||
|
int expectedValues[] = { 12629351, 12266245, 11883254, 11479288, 11053198, 10603773,
|
||||||
|
10129735, 9629735, 9102352, 8546087, 7959357, 7340494, 6687738, 5999234, 5273023,
|
||||||
|
4507040, 3699107, 2846927, 1948077, 1000000 };
|
||||||
|
|
||||||
|
long current = DateHelper.getStartOfToday();
|
||||||
|
for(int expectedValue : expectedValues)
|
||||||
|
{
|
||||||
|
assertThat(habit.scores.getValue(current), equalTo(expectedValue));
|
||||||
|
current -= DateHelper.millisecondsInOneDay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getAllValues_withoutGroups()
|
||||||
|
{
|
||||||
|
toggleRepetitions(0, 20);
|
||||||
|
|
||||||
|
int expectedValues[] = { 12629351, 12266245, 11883254, 11479288, 11053198, 10603773,
|
||||||
|
10129735, 9629735, 9102352, 8546087, 7959357, 7340494, 6687738, 5999234, 5273023,
|
||||||
|
4507040, 3699107, 2846927, 1948077, 1000000 };
|
||||||
|
|
||||||
|
int actualValues[] = habit.scores.getAllValues(1);
|
||||||
|
assertThat(actualValues, equalTo(expectedValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getAllValues_withGroups()
|
||||||
|
{
|
||||||
|
toggleRepetitions(0, 20);
|
||||||
|
|
||||||
|
int expectedValues[] = { 11434978, 7894999, 3212362 };
|
||||||
|
|
||||||
|
int actualValues[] = habit.scores.getAllValues(7);
|
||||||
|
assertThat(actualValues, equalTo(expectedValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_writeCSV() throws IOException
|
||||||
|
{
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
Habit habit = HabitFixtures.createShortHabit();
|
||||||
|
|
||||||
|
String expectedCSV =
|
||||||
|
"2015-01-16,0.0519\n" +
|
||||||
|
"2015-01-17,0.1021\n" +
|
||||||
|
"2015-01-18,0.0986\n" +
|
||||||
|
"2015-01-19,0.0952\n" +
|
||||||
|
"2015-01-20,0.1439\n" +
|
||||||
|
"2015-01-21,0.1909\n" +
|
||||||
|
"2015-01-22,0.2364\n" +
|
||||||
|
"2015-01-23,0.2283\n" +
|
||||||
|
"2015-01-24,0.2205\n" +
|
||||||
|
"2015-01-25,0.2649\n";
|
||||||
|
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
habit.scores.writeCSV(writer);
|
||||||
|
|
||||||
|
assertThat(writer.toString(), equalTo(expectedCSV));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleRepetitions(final int from, final int to)
|
||||||
|
{
|
||||||
|
DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void execute()
|
||||||
|
{
|
||||||
|
long today = DateHelper.getStartOfToday();
|
||||||
|
for (int i = from; i < to; i++)
|
||||||
|
habit.repetitions.toggle(today - i * DateHelper.millisecondsInOneDay);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.models;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.models.Checkmark;
|
||||||
|
import org.isoron.uhabits.models.Score;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class ScoreTest extends BaseTest
|
||||||
|
{
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_compute_withDailyHabit()
|
||||||
|
{
|
||||||
|
int checkmark = Checkmark.UNCHECKED;
|
||||||
|
assertThat(Score.compute(1, 0, checkmark), equalTo(0));
|
||||||
|
assertThat(Score.compute(1, 5000000, checkmark), equalTo(4740387));
|
||||||
|
assertThat(Score.compute(1, 10000000, checkmark), equalTo(9480775));
|
||||||
|
assertThat(Score.compute(1, Score.MAX_VALUE, checkmark), equalTo(18259478));
|
||||||
|
|
||||||
|
checkmark = Checkmark.CHECKED_IMPLICITLY;
|
||||||
|
assertThat(Score.compute(1, 0, checkmark), equalTo(0));
|
||||||
|
assertThat(Score.compute(1, 5000000, checkmark), equalTo(4740387));
|
||||||
|
assertThat(Score.compute(1, 10000000, checkmark), equalTo(9480775));
|
||||||
|
assertThat(Score.compute(1, Score.MAX_VALUE, checkmark), equalTo(18259478));
|
||||||
|
|
||||||
|
checkmark = Checkmark.CHECKED_EXPLICITLY;
|
||||||
|
assertThat(Score.compute(1, 0, checkmark), equalTo(1000000));
|
||||||
|
assertThat(Score.compute(1, 5000000, checkmark), equalTo(5740387));
|
||||||
|
assertThat(Score.compute(1, 10000000, checkmark), equalTo(10480775));
|
||||||
|
assertThat(Score.compute(1, Score.MAX_VALUE, checkmark), equalTo(Score.MAX_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_compute_withNonDailyHabit()
|
||||||
|
{
|
||||||
|
int checkmark = Checkmark.CHECKED_EXPLICITLY;
|
||||||
|
assertThat(Score.compute(1/3.0, 0, checkmark), equalTo(1000000));
|
||||||
|
assertThat(Score.compute(1/3.0, 5000000, checkmark), equalTo(5916180));
|
||||||
|
assertThat(Score.compute(1/3.0, 10000000, checkmark), equalTo(10832360));
|
||||||
|
assertThat(Score.compute(1/3.0, Score.MAX_VALUE, checkmark), equalTo(Score.MAX_VALUE));
|
||||||
|
|
||||||
|
assertThat(Score.compute(1/7.0, 0, checkmark), equalTo(1000000));
|
||||||
|
assertThat(Score.compute(1/7.0, 5000000, checkmark), equalTo(5964398));
|
||||||
|
assertThat(Score.compute(1/7.0, 10000000, checkmark), equalTo(10928796));
|
||||||
|
assertThat(Score.compute(1/7.0, Score.MAX_VALUE, checkmark), equalTo(Score.MAX_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getStarStatus()
|
||||||
|
{
|
||||||
|
Score s = new Score();
|
||||||
|
|
||||||
|
s.score = Score.FULL_STAR_CUTOFF + 1;
|
||||||
|
assertThat(s.getStarStatus(), equalTo(Score.FULL_STAR));
|
||||||
|
|
||||||
|
s.score = Score.FULL_STAR_CUTOFF;
|
||||||
|
assertThat(s.getStarStatus(), equalTo(Score.FULL_STAR));
|
||||||
|
|
||||||
|
s.score = Score.FULL_STAR_CUTOFF - 1;
|
||||||
|
assertThat(s.getStarStatus(), equalTo(Score.HALF_STAR));
|
||||||
|
|
||||||
|
s.score = Score.HALF_STAR_CUTOFF + 1;
|
||||||
|
assertThat(s.getStarStatus(), equalTo(Score.HALF_STAR));
|
||||||
|
|
||||||
|
s.score = Score.HALF_STAR_CUTOFF;
|
||||||
|
assertThat(s.getStarStatus(), equalTo(Score.HALF_STAR));
|
||||||
|
|
||||||
|
s.score = Score.HALF_STAR_CUTOFF - 1;
|
||||||
|
assertThat(s.getStarStatus(), equalTo(Score.EMPTY_STAR));
|
||||||
|
|
||||||
|
s.score = 0;
|
||||||
|
assertThat(s.getStarStatus(), equalTo(Score.EMPTY_STAR));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.tasks;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.tasks.ExportCSVTask;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
import static org.hamcrest.core.IsNot.not;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class ExportCSVTaskTest extends BaseTest
|
||||||
|
{
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExportCSV() throws Throwable
|
||||||
|
{
|
||||||
|
HabitFixtures.createShortHabit();
|
||||||
|
List<Habit> habits = Habit.getAll(true);
|
||||||
|
ProgressBar bar = new ProgressBar(targetContext);
|
||||||
|
|
||||||
|
ExportCSVTask task = new ExportCSVTask(habits, bar);
|
||||||
|
task.setListener(new ExportCSVTask.Listener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onExportCSVFinished(String archiveFilename)
|
||||||
|
{
|
||||||
|
assertThat(archiveFilename, is(not(nullValue())));
|
||||||
|
|
||||||
|
File f = new File(archiveFilename);
|
||||||
|
assertTrue(f.exists());
|
||||||
|
assertTrue(f.canRead());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
task.execute();
|
||||||
|
waitForAsyncTasks();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.tasks;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.tasks.ExportDBTask;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
import static org.hamcrest.core.IsNot.not;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class ExportDBTaskTest extends BaseTest
|
||||||
|
{
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExportCSV() throws Throwable
|
||||||
|
{
|
||||||
|
Context context = InstrumentationRegistry.getContext();
|
||||||
|
|
||||||
|
ProgressBar bar = new ProgressBar(context);
|
||||||
|
ExportDBTask task = new ExportDBTask(bar);
|
||||||
|
task.setListener(new ExportDBTask.Listener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onExportDBFinished(String filename)
|
||||||
|
{
|
||||||
|
assertThat(filename, is(not(nullValue())));
|
||||||
|
|
||||||
|
File f = new File(filename);
|
||||||
|
assertTrue(f.exists());
|
||||||
|
assertTrue(f.canRead());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
task.execute();
|
||||||
|
waitForAsyncTasks();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.tasks;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||||
|
import org.isoron.uhabits.tasks.ImportDataTask;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class ImportDataTaskTest extends BaseTest
|
||||||
|
{
|
||||||
|
private File baseDir;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
baseDir = DatabaseHelper.getFilesDir("Backups");
|
||||||
|
if(baseDir == null) fail("baseDir should not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyAssetToFile(String assetPath, File dst) throws IOException
|
||||||
|
{
|
||||||
|
InputStream in = testContext.getAssets().open(assetPath);
|
||||||
|
DatabaseHelper.copy(in, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTaskResult(final int expectedResult, String assetFilename) throws Throwable
|
||||||
|
{
|
||||||
|
ImportDataTask task = createTask(assetFilename);
|
||||||
|
|
||||||
|
task.setListener(new ImportDataTask.Listener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onImportFinished(int result)
|
||||||
|
{
|
||||||
|
assertThat(result, equalTo(expectedResult));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
task.execute();
|
||||||
|
waitForAsyncTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private ImportDataTask createTask(String assetFilename) throws IOException
|
||||||
|
{
|
||||||
|
ProgressBar bar = new ProgressBar(targetContext);
|
||||||
|
File file = new File(String.format("%s/%s", baseDir.getPath(), assetFilename));
|
||||||
|
copyAssetToFile(assetFilename, file);
|
||||||
|
|
||||||
|
return new ImportDataTask(file, bar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImportInvalidData() throws Throwable
|
||||||
|
{
|
||||||
|
assertTaskResult(ImportDataTask.NOT_RECOGNIZED, "icon.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImportValidData() throws Throwable
|
||||||
|
{
|
||||||
|
assertTaskResult(ImportDataTask.SUCCESS, "loop.db");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.views;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.isoron.uhabits.views.CheckmarkView;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class CheckmarkViewTest extends ViewTest
|
||||||
|
{
|
||||||
|
private CheckmarkView view;
|
||||||
|
private Habit habit;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
habit = HabitFixtures.createShortHabit();
|
||||||
|
view = new CheckmarkView(targetContext);
|
||||||
|
view.setHabit(habit);
|
||||||
|
refreshData(view);
|
||||||
|
measureView(dpToPixels(100), dpToPixels(200), view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_checked() throws IOException
|
||||||
|
{
|
||||||
|
assertRenders(view, "CheckmarkView/checked.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_unchecked() throws IOException
|
||||||
|
{
|
||||||
|
habit.repetitions.toggle(DateHelper.getStartOfToday());
|
||||||
|
view.refreshData();
|
||||||
|
|
||||||
|
assertRenders(view, "CheckmarkView/unchecked.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_implicitlyChecked() throws IOException
|
||||||
|
{
|
||||||
|
long today = DateHelper.getStartOfToday();
|
||||||
|
long day = DateHelper.millisecondsInOneDay;
|
||||||
|
habit.repetitions.toggle(today);
|
||||||
|
habit.repetitions.toggle(today - day);
|
||||||
|
habit.repetitions.toggle(today - 2 * day);
|
||||||
|
view.refreshData();
|
||||||
|
|
||||||
|
assertRenders(view, "CheckmarkView/implicitly_checked.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_largeSize() throws IOException
|
||||||
|
{
|
||||||
|
measureView(dpToPixels(300), dpToPixels(300), view);
|
||||||
|
assertRenders(view, "CheckmarkView/large_size.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.views;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.isoron.uhabits.views.HabitFrequencyView;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class HabitFrequencyViewTest extends ViewTest
|
||||||
|
{
|
||||||
|
private HabitFrequencyView view;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
Habit habit = HabitFixtures.createLongHabit();
|
||||||
|
|
||||||
|
view = new HabitFrequencyView(targetContext);
|
||||||
|
view.setHabit(habit);
|
||||||
|
refreshData(view);
|
||||||
|
measureView(dpToPixels(300), dpToPixels(100), view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender() throws Throwable
|
||||||
|
{
|
||||||
|
assertRenders(view, "HabitFrequencyView/render.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withTransparentBackground() throws Throwable
|
||||||
|
{
|
||||||
|
view.setIsBackgroundTransparent(true);
|
||||||
|
assertRenders(view, "HabitFrequencyView/renderTransparent.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withDifferentSize() throws Throwable
|
||||||
|
{
|
||||||
|
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||||
|
assertRenders(view, "HabitFrequencyView/renderDifferentSize.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withDataOffset() throws Throwable
|
||||||
|
{
|
||||||
|
view.onScroll(null, null, -dpToPixels(150), 0);
|
||||||
|
view.invalidate();
|
||||||
|
|
||||||
|
assertRenders(view, "HabitFrequencyView/renderDataOffset.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.views;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.isoron.uhabits.views.HabitHistoryView;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertFalse;
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class HabitHistoryViewTest extends ViewTest
|
||||||
|
{
|
||||||
|
private Habit habit;
|
||||||
|
private HabitHistoryView view;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
habit = HabitFixtures.createLongHabit();
|
||||||
|
|
||||||
|
view = new HabitHistoryView(targetContext);
|
||||||
|
view.setHabit(habit);
|
||||||
|
measureView(dpToPixels(300), dpToPixels(100), view);
|
||||||
|
refreshData(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender() throws Throwable
|
||||||
|
{
|
||||||
|
assertRenders(view, "HabitHistoryView/render.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withTransparentBackground() throws Throwable
|
||||||
|
{
|
||||||
|
view.setIsBackgroundTransparent(true);
|
||||||
|
assertRenders(view, "HabitHistoryView/renderTransparent.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withDifferentSize() throws Throwable
|
||||||
|
{
|
||||||
|
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||||
|
assertRenders(view, "HabitHistoryView/renderDifferentSize.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withDataOffset() throws Throwable
|
||||||
|
{
|
||||||
|
view.onScroll(null, null, -dpToPixels(150), 0);
|
||||||
|
view.invalidate();
|
||||||
|
|
||||||
|
assertRenders(view, "HabitHistoryView/renderDataOffset.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tapDate_withEditableView() throws Throwable
|
||||||
|
{
|
||||||
|
view.setIsEditable(true);
|
||||||
|
tap(view, 270, 30);
|
||||||
|
waitForAsyncTasks();
|
||||||
|
|
||||||
|
long today = DateHelper.getStartOfToday();
|
||||||
|
assertFalse(habit.repetitions.contains(today));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tapDate_atInvalidLocations() throws Throwable
|
||||||
|
{
|
||||||
|
int expectedCheckmarkValues[] = habit.checkmarks.getAllValues();
|
||||||
|
|
||||||
|
view.setIsEditable(true);
|
||||||
|
tap(view, 45, 5); // header
|
||||||
|
tap(view, 270, 43); // tomorrow's square
|
||||||
|
tap(view, 280, 30); // right axis
|
||||||
|
waitForAsyncTasks();
|
||||||
|
|
||||||
|
int actualCheckmarkValues[] = habit.checkmarks.getAllValues();
|
||||||
|
assertThat(actualCheckmarkValues, equalTo(expectedCheckmarkValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tapDate_withReadOnlyView() throws Throwable
|
||||||
|
{
|
||||||
|
view.setIsEditable(false);
|
||||||
|
tap(view, 270, 30);
|
||||||
|
waitForAsyncTasks();
|
||||||
|
|
||||||
|
long today = DateHelper.getStartOfToday();
|
||||||
|
assertTrue(habit.repetitions.contains(today));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.views;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.isoron.uhabits.views.HabitScoreView;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class HabitScoreViewTest extends ViewTest
|
||||||
|
{
|
||||||
|
private Habit habit;
|
||||||
|
private HabitScoreView view;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
habit = HabitFixtures.createLongHabit();
|
||||||
|
|
||||||
|
view = new HabitScoreView(targetContext);
|
||||||
|
view.setHabit(habit);
|
||||||
|
view.setBucketSize(7);
|
||||||
|
refreshData(view);
|
||||||
|
measureView(dpToPixels(300), dpToPixels(100), view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender() throws Throwable
|
||||||
|
{
|
||||||
|
Log.d("HabitScoreViewTest", String.format("height=%d", dpToPixels(100)));
|
||||||
|
assertRenders(view, "HabitScoreView/render.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withTransparentBackground() throws Throwable
|
||||||
|
{
|
||||||
|
view.setIsBackgroundTransparent(true);
|
||||||
|
assertRenders(view, "HabitScoreView/renderTransparent.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withDifferentSize() throws Throwable
|
||||||
|
{
|
||||||
|
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||||
|
assertRenders(view, "HabitScoreView/renderDifferentSize.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withDataOffset() throws Throwable
|
||||||
|
{
|
||||||
|
view.onScroll(null, null, -dpToPixels(150), 0);
|
||||||
|
view.invalidate();
|
||||||
|
|
||||||
|
assertRenders(view, "HabitScoreView/renderDataOffset.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withMonthlyBucket() throws Throwable
|
||||||
|
{
|
||||||
|
view.setBucketSize(30);
|
||||||
|
view.refreshData();
|
||||||
|
view.invalidate();
|
||||||
|
|
||||||
|
assertRenders(view, "HabitScoreView/renderMonthly.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withYearlyBucket() throws Throwable
|
||||||
|
{
|
||||||
|
view.setBucketSize(365);
|
||||||
|
view.refreshData();
|
||||||
|
view.invalidate();
|
||||||
|
|
||||||
|
assertRenders(view, "HabitScoreView/renderYearly.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.views;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
|
import org.isoron.uhabits.views.HabitStreakView;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class HabitStreakViewTest extends ViewTest
|
||||||
|
{
|
||||||
|
private HabitStreakView view;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
Habit habit = HabitFixtures.createLongHabit();
|
||||||
|
|
||||||
|
view = new HabitStreakView(targetContext);
|
||||||
|
measureView(dpToPixels(300), dpToPixels(100), view);
|
||||||
|
|
||||||
|
view.setHabit(habit);
|
||||||
|
refreshData(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender() throws Throwable
|
||||||
|
{
|
||||||
|
assertRenders(view, "HabitStreakView/render.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withTransparentBackground() throws Throwable
|
||||||
|
{
|
||||||
|
view.setIsBackgroundTransparent(true);
|
||||||
|
assertRenders(view, "HabitStreakView/renderTransparent.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withSmallSize() throws Throwable
|
||||||
|
{
|
||||||
|
measureView(dpToPixels(100), dpToPixels(100), view);
|
||||||
|
refreshData(view);
|
||||||
|
|
||||||
|
assertRenders(view, "HabitStreakView/renderSmallSize.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.views;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
|
import org.isoron.uhabits.views.NumberView;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class NumberViewTest extends ViewTest
|
||||||
|
{
|
||||||
|
private NumberView view;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
view = new NumberView(targetContext);
|
||||||
|
view.setLabel("Hello world");
|
||||||
|
view.setNumber(31);
|
||||||
|
view.setColor(ColorHelper.palette[0]);
|
||||||
|
measureView(dpToPixels(100), dpToPixels(100), view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_base() throws IOException
|
||||||
|
{
|
||||||
|
assertRenders(view, "NumberView/render.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withLongLabel() throws IOException
|
||||||
|
{
|
||||||
|
view.setLabel("The quick brown fox jumps over the lazy fox");
|
||||||
|
|
||||||
|
measureView(dpToPixels(100), dpToPixels(100), view);
|
||||||
|
assertRenders(view, "NumberView/renderLongLabel.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withDifferentParams() throws IOException
|
||||||
|
{
|
||||||
|
view.setNumber(500);
|
||||||
|
view.setColor(ColorHelper.palette[5]);
|
||||||
|
view.setTextSize(targetContext.getResources().getDimension(R.dimen.tinyTextSize));
|
||||||
|
|
||||||
|
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||||
|
assertRenders(view, "NumberView/renderDifferentParams.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.views;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
|
import org.isoron.uhabits.views.RingView;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class RingViewTest extends ViewTest
|
||||||
|
{
|
||||||
|
private RingView view;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
view = new RingView(targetContext);
|
||||||
|
view.setLabel("Hello world");
|
||||||
|
view.setPercentage(0.6f);
|
||||||
|
view.setColor(ColorHelper.palette[0]);
|
||||||
|
view.setMaxDiameter(dpToPixels(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_base() throws IOException
|
||||||
|
{
|
||||||
|
measureView(dpToPixels(100), dpToPixels(100), view);
|
||||||
|
assertRenders(view, "RingView/render.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withLongLabel() throws IOException
|
||||||
|
{
|
||||||
|
view.setLabel("The quick brown fox jumps over the lazy fox");
|
||||||
|
|
||||||
|
measureView(dpToPixels(100), dpToPixels(100), view);
|
||||||
|
assertRenders(view, "RingView/renderLongLabel.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_withDifferentParams() throws IOException
|
||||||
|
{
|
||||||
|
view.setLabel("Habit Strength");
|
||||||
|
view.setPercentage(0.25f);
|
||||||
|
view.setMaxDiameter(dpToPixels(50));
|
||||||
|
view.setColor(ColorHelper.palette[5]);
|
||||||
|
|
||||||
|
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||||
|
assertRenders(view, "RingView/renderDifferentParams.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.views;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.view.GestureDetector;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseTest;
|
||||||
|
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||||
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
|
import org.isoron.uhabits.tasks.BaseTask;
|
||||||
|
import org.isoron.uhabits.views.HabitDataView;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.fail;
|
||||||
|
|
||||||
|
public class ViewTest extends BaseTest
|
||||||
|
{
|
||||||
|
protected static final double SIMILARITY_CUTOFF = 0.09;
|
||||||
|
public static final int HISTOGRAM_BIN_SIZE = 8;
|
||||||
|
|
||||||
|
protected void measureView(int width, int height, View view)
|
||||||
|
{
|
||||||
|
int specWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
|
||||||
|
int specHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
|
||||||
|
|
||||||
|
view.measure(specWidth, specHeight);
|
||||||
|
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertRenders(View view, String expectedImagePath) throws IOException
|
||||||
|
{
|
||||||
|
StringBuilder errorMessage = new StringBuilder();
|
||||||
|
expectedImagePath = getVersionedViewAssetPath(expectedImagePath);
|
||||||
|
|
||||||
|
view.setDrawingCacheEnabled(true);
|
||||||
|
view.buildDrawingCache();
|
||||||
|
Bitmap actual = view.getDrawingCache();
|
||||||
|
Bitmap expected = getBitmapFromAssets(expectedImagePath);
|
||||||
|
|
||||||
|
int width = actual.getWidth();
|
||||||
|
int height = actual.getHeight();
|
||||||
|
Bitmap scaledExpected = Bitmap.createScaledBitmap(expected, width, height, true);
|
||||||
|
|
||||||
|
double distance;
|
||||||
|
boolean similarEnough = true;
|
||||||
|
|
||||||
|
if ((distance = compareHistograms(getHistogram(actual), getHistogram(scaledExpected))) > SIMILARITY_CUTOFF)
|
||||||
|
{
|
||||||
|
similarEnough = false;
|
||||||
|
errorMessage.append(String.format(
|
||||||
|
"Rendered image has wrong histogram (distance=%f). ",
|
||||||
|
distance));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!similarEnough)
|
||||||
|
{
|
||||||
|
saveBitmap(expectedImagePath, ".scaledExpected", scaledExpected);
|
||||||
|
String path = saveBitmap(expectedImagePath, ".actual", actual);
|
||||||
|
errorMessage.append(String.format("Actual rendered image " + "saved to %s", path));
|
||||||
|
fail(errorMessage.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
actual.recycle();
|
||||||
|
expected.recycle();
|
||||||
|
scaledExpected.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap getBitmapFromAssets(String path) throws IOException
|
||||||
|
{
|
||||||
|
InputStream stream = testContext.getAssets().open(path);
|
||||||
|
return BitmapFactory.decodeStream(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getVersionedViewAssetPath(String path)
|
||||||
|
{
|
||||||
|
String result = null;
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 21)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String vpath = "views-v21/" + path;
|
||||||
|
testContext.getAssets().open(vpath);
|
||||||
|
result = vpath;
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result == null)
|
||||||
|
result = "views/" + path;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String saveBitmap(String filename, String suffix, Bitmap bitmap)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
File dir = DatabaseHelper.getSDCardDir("test-screenshots");
|
||||||
|
if(dir == null) dir = DatabaseHelper.getFilesDir("test-screenshots");
|
||||||
|
if(dir == null) throw new RuntimeException("Could not find suitable dir for screenshots");
|
||||||
|
|
||||||
|
filename = filename.replaceAll("\\.png$", suffix + ".png");
|
||||||
|
String absolutePath = String.format("%s/%s", dir.getAbsolutePath(), filename);
|
||||||
|
|
||||||
|
File parent = new File(absolutePath).getParentFile();
|
||||||
|
if(!parent.exists() && !parent.mkdirs())
|
||||||
|
throw new RuntimeException(String.format("Could not create dir: %s",
|
||||||
|
parent.getAbsolutePath()));
|
||||||
|
|
||||||
|
FileOutputStream out = new FileOutputStream(absolutePath);
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||||
|
|
||||||
|
return absolutePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[][] getHistogram(Bitmap bitmap)
|
||||||
|
{
|
||||||
|
int histogram[][] = new int[4][256 / HISTOGRAM_BIN_SIZE];
|
||||||
|
|
||||||
|
for(int x = 0; x < bitmap.getWidth(); x++)
|
||||||
|
{
|
||||||
|
for(int y = 0; y < bitmap.getHeight(); y++)
|
||||||
|
{
|
||||||
|
int color = bitmap.getPixel(x, y);
|
||||||
|
int[] argb = new int[]{
|
||||||
|
(color >> 24) & 0xff, //alpha
|
||||||
|
(color >> 16) & 0xff, //red
|
||||||
|
(color >> 8) & 0xff, //green
|
||||||
|
(color ) & 0xff //blue
|
||||||
|
};
|
||||||
|
|
||||||
|
histogram[0][argb[0] / HISTOGRAM_BIN_SIZE]++;
|
||||||
|
histogram[1][argb[1] / HISTOGRAM_BIN_SIZE]++;
|
||||||
|
histogram[2][argb[2] / HISTOGRAM_BIN_SIZE]++;
|
||||||
|
histogram[3][argb[3] / HISTOGRAM_BIN_SIZE]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return histogram;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double compareHistograms(int[][] actualHistogram, int[][] expectedHistogram)
|
||||||
|
{
|
||||||
|
long diff = 0;
|
||||||
|
long total = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < 256 / HISTOGRAM_BIN_SIZE; i ++)
|
||||||
|
{
|
||||||
|
diff += Math.abs(actualHistogram[0][i] - expectedHistogram[0][i]);
|
||||||
|
diff += Math.abs(actualHistogram[1][i] - expectedHistogram[1][i]);
|
||||||
|
diff += Math.abs(actualHistogram[2][i] - expectedHistogram[2][i]);
|
||||||
|
diff += Math.abs(actualHistogram[3][i] - expectedHistogram[3][i]);
|
||||||
|
|
||||||
|
total += actualHistogram[0][i];
|
||||||
|
total += actualHistogram[1][i];
|
||||||
|
total += actualHistogram[2][i];
|
||||||
|
total += actualHistogram[3][i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (double) diff / total / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int dpToPixels(int dp)
|
||||||
|
{
|
||||||
|
return (int) UIHelper.dpToPixels(targetContext, dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void tap(GestureDetector.OnGestureListener view, int x, int y) throws InterruptedException
|
||||||
|
{
|
||||||
|
long now = SystemClock.uptimeMillis();
|
||||||
|
MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, dpToPixels(x),
|
||||||
|
dpToPixels(y), 0);
|
||||||
|
view.onSingleTapUp(e);
|
||||||
|
e.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void refreshData(final HabitDataView view)
|
||||||
|
{
|
||||||
|
new BaseTask()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected void doInBackground()
|
||||||
|
{
|
||||||
|
view.refreshData();
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
waitForAsyncTasks();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Time out");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/src/debug/AndroidManifest.xml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<manifest
|
||||||
|
package="org.isoron.uhabits"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.SET_ANIMATION_SCALE"/>
|
||||||
|
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
|
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||||
|
tools:replace="maxSdkVersion"
|
||||||
|
android:maxSdkVersion="99" />
|
||||||
|
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
tools:replace="maxSdkVersion"
|
||||||
|
android:maxSdkVersion="99" />
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -21,21 +21,21 @@
|
|||||||
<manifest
|
<manifest
|
||||||
package="org.isoron.uhabits"
|
package="org.isoron.uhabits"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:versionCode="12"
|
android:versionCode="14"
|
||||||
android:versionName="1.3.2">
|
android:versionName="1.4.0">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
|
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="18"/>
|
android:maxSdkVersion="18" />
|
||||||
|
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="18"/>
|
android:maxSdkVersion="18" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="com.activeandroid.app.Application"
|
android:name="HabitsApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:backupAgent=".HabitsBackupAgent"
|
android:backupAgent=".HabitsBackupAgent"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
@@ -43,12 +43,6 @@
|
|||||||
android:theme="@style/AppBaseTheme"
|
android:theme="@style/AppBaseTheme"
|
||||||
android:supportsRtl="true">
|
android:supportsRtl="true">
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="AA_DB_NAME"
|
|
||||||
android:value="uhabits.db"/>
|
|
||||||
<meta-data
|
|
||||||
android:name="AA_DB_VERSION"
|
|
||||||
android:value="12"/>
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.backup.api_key"
|
android:name="com.google.android.backup.api_key"
|
||||||
android:value="AEdPqrEAAAAI6aeWncbnMNo8E5GWeZ44dlc5cQ7tCROwFhOtiw"/>
|
android:value="AEdPqrEAAAAI6aeWncbnMNo8E5GWeZ44dlc5cQ7tCROwFhOtiw"/>
|
||||||
|
|||||||
4
app/src/main/assets/migrations/13.sql
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
create index idx_score_habit_timestamp on score(habit, timestamp);
|
||||||
|
create index idx_checkmark_habit_timestamp on checkmarks(habit, timestamp);
|
||||||
|
create index idx_repetitions_habit_timestamp on repetitions(habit, timestamp);
|
||||||
|
create index idx_streak_habit_end on streak(habit, end);
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.helpers;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
import android.os.Vibrator;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.util.DisplayMetrics;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.BuildConfig;
|
|
||||||
|
|
||||||
public abstract class DialogHelper
|
|
||||||
{
|
|
||||||
|
|
||||||
public static final String ISORON_NAMESPACE = "http://isoron.org/android";
|
|
||||||
private static Typeface fontawesome;
|
|
||||||
|
|
||||||
public interface OnSavedListener
|
|
||||||
{
|
|
||||||
void onSaved(Command command, Object savedObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void showSoftKeyboard(View view)
|
|
||||||
{
|
|
||||||
InputMethodManager imm = (InputMethodManager) view.getContext()
|
|
||||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
||||||
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void vibrate(Context context, int duration)
|
|
||||||
{
|
|
||||||
Vibrator vb = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
|
||||||
vb.vibrate(duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static void incrementLaunchCount(Context context)
|
|
||||||
{
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
int count = prefs.getInt("launch_count", 0);
|
|
||||||
prefs.edit().putInt("launch_count", count + 1).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void updateLastAppVersion(Context context)
|
|
||||||
{
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
prefs.edit().putInt("last_version", BuildConfig.VERSION_CODE).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getLaunchCount(Context context)
|
|
||||||
{
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
return prefs.getInt("launch_count", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getAttribute(Context context, AttributeSet attrs, String name)
|
|
||||||
{
|
|
||||||
int resId = attrs.getAttributeResourceValue(ISORON_NAMESPACE, name, 0);
|
|
||||||
|
|
||||||
if(resId != 0)
|
|
||||||
return context.getResources().getString(resId);
|
|
||||||
else
|
|
||||||
return attrs.getAttributeValue(ISORON_NAMESPACE, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float dpToPixels(Context context, float dp)
|
|
||||||
{
|
|
||||||
Resources resources = context.getResources();
|
|
||||||
DisplayMetrics metrics = resources.getDisplayMetrics();
|
|
||||||
return dp * (metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -28,7 +28,7 @@ import android.os.Bundle;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.isoron.helpers.ColorHelper;
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
|
|
||||||
public class AboutActivity extends Activity implements View.OnClickListener
|
public class AboutActivity extends Activity implements View.OnClickListener
|
||||||
{
|
{
|
||||||
@@ -68,7 +68,7 @@ public class AboutActivity extends Activity implements View.OnClickListener
|
|||||||
{
|
{
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse("market://details?id=org.isoron.uhabits"));
|
intent.setData(Uri.parse(getString(R.string.playStoreURL)));
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -77,8 +77,7 @@ public class AboutActivity extends Activity implements View.OnClickListener
|
|||||||
{
|
{
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(Intent.ACTION_SENDTO);
|
intent.setAction(Intent.ACTION_SENDTO);
|
||||||
intent.setData(Uri.parse("mailto:isoron+habits@gmail.com?" +
|
intent.setData(Uri.parse(getString(R.string.feedbackURL)));
|
||||||
"subject=Feedback%20about%20Loop%20Habit%20Tracker"));
|
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -87,7 +86,7 @@ public class AboutActivity extends Activity implements View.OnClickListener
|
|||||||
{
|
{
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse("https://github.com/iSoron/uhabits"));
|
intent.setData(Uri.parse(getString(R.string.sourceCodeURL)));
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.helpers;
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.backup.BackupManager;
|
import android.app.backup.BackupManager;
|
||||||
@@ -25,11 +25,11 @@ import android.os.AsyncTask;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.commands.Command;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
abstract public class ReplayableActivity extends Activity
|
abstract public class BaseActivity extends Activity implements Thread.UncaughtExceptionHandler
|
||||||
{
|
{
|
||||||
private static int MAX_UNDO_LEVEL = 15;
|
private static int MAX_UNDO_LEVEL = 15;
|
||||||
|
|
||||||
@@ -37,11 +37,16 @@ abstract public class ReplayableActivity extends Activity
|
|||||||
private LinkedList<Command> redoList;
|
private LinkedList<Command> redoList;
|
||||||
private Toast toast;
|
private Toast toast;
|
||||||
|
|
||||||
|
Thread.UncaughtExceptionHandler androidExceptionHandler;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState)
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
androidExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(this);
|
||||||
|
|
||||||
undoList = new LinkedList<>();
|
undoList = new LinkedList<>();
|
||||||
redoList = new LinkedList<>();
|
redoList = new LinkedList<>();
|
||||||
}
|
}
|
||||||
@@ -103,7 +108,7 @@ abstract public class ReplayableActivity extends Activity
|
|||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void aVoid)
|
protected void onPostExecute(Void aVoid)
|
||||||
{
|
{
|
||||||
ReplayableActivity.this.onPostExecuteCommand(refreshKey);
|
BaseActivity.this.onPostExecuteCommand(refreshKey);
|
||||||
BackupManager.dataChanged("org.isoron.uhabits");
|
BackupManager.dataChanged("org.isoron.uhabits");
|
||||||
}
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
@@ -115,4 +120,23 @@ abstract public class ReplayableActivity extends Activity
|
|||||||
public void onPostExecuteCommand(Long refreshKey)
|
public void onPostExecuteCommand(Long refreshKey)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uncaughtException(Thread thread, Throwable ex)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ex.printStackTrace();
|
||||||
|
HabitsApplication.generateLogFile();
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
if(androidExceptionHandler != null)
|
||||||
|
androidExceptionHandler.uncaughtException(thread, ex);
|
||||||
|
else
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -36,9 +36,11 @@ import android.preference.PreferenceManager;
|
|||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import org.isoron.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||||
|
import org.isoron.uhabits.models.Checkmark;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.tasks.BaseTask;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
@@ -124,11 +126,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
|||||||
|
|
||||||
private void dismissAllHabits()
|
private void dismissAllHabits()
|
||||||
{
|
{
|
||||||
for (Habit h : Habit.getHighlightedHabits())
|
|
||||||
{
|
|
||||||
h.highlight = 0;
|
|
||||||
h.save();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dismissNotification(Context context, Long habitId)
|
private void dismissNotification(Context context, Long habitId)
|
||||||
@@ -141,62 +139,76 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void createNotification(Context context, Intent intent)
|
private void createNotification(final Context context, final Intent intent)
|
||||||
{
|
{
|
||||||
Uri data = intent.getData();
|
final Uri data = intent.getData();
|
||||||
Habit habit = Habit.get(ContentUris.parseId(data));
|
final Habit habit = Habit.get(ContentUris.parseId(data));
|
||||||
Long timestamp = intent.getLongExtra("timestamp", DateHelper.getStartOfToday());
|
final Long timestamp = intent.getLongExtra("timestamp", DateHelper.getStartOfToday());
|
||||||
Long reminderTime = intent.getLongExtra("reminderTime", DateHelper.getStartOfToday());
|
final Long reminderTime = intent.getLongExtra("reminderTime", DateHelper.getStartOfToday());
|
||||||
|
|
||||||
if (habit == null) return;
|
if (habit == null) return;
|
||||||
if (habit.repetitions.hasImplicitRepToday()) return;
|
|
||||||
|
|
||||||
habit.highlight = 1;
|
new BaseTask()
|
||||||
habit.save();
|
{
|
||||||
|
int todayValue;
|
||||||
|
|
||||||
if (!checkWeekday(intent, habit)) return;
|
@Override
|
||||||
|
protected void doInBackground()
|
||||||
|
{
|
||||||
|
todayValue = habit.checkmarks.getTodayValue();
|
||||||
|
}
|
||||||
|
|
||||||
// Check if reminder has been turned off after alarm was scheduled
|
@Override
|
||||||
if (habit.reminderHour == null) return;
|
protected void onPostExecute(Void aVoid)
|
||||||
|
{
|
||||||
|
if (todayValue != Checkmark.UNCHECKED) return;
|
||||||
|
if (!checkWeekday(intent, habit)) return;
|
||||||
|
|
||||||
Intent contentIntent = new Intent(context, MainActivity.class);
|
// Check if reminder has been turned off after alarm was scheduled
|
||||||
contentIntent.setData(data);
|
if (habit.reminderHour == null) return;
|
||||||
PendingIntent contentPendingIntent =
|
|
||||||
PendingIntent.getActivity(context, 0, contentIntent, 0);
|
|
||||||
|
|
||||||
PendingIntent dismissPendingIntent = buildDismissIntent(context);
|
Intent contentIntent = new Intent(context, MainActivity.class);
|
||||||
PendingIntent checkIntentPending = buildCheckIntent(context, habit, timestamp);
|
contentIntent.setData(data);
|
||||||
PendingIntent snoozeIntentPending = buildSnoozeIntent(context, habit);
|
PendingIntent contentPendingIntent =
|
||||||
|
PendingIntent.getActivity(context, 0, contentIntent, 0);
|
||||||
|
|
||||||
Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
PendingIntent dismissPendingIntent = buildDismissIntent(context);
|
||||||
|
PendingIntent checkIntentPending = buildCheckIntent(context, habit, timestamp);
|
||||||
|
PendingIntent snoozeIntentPending = buildSnoozeIntent(context, habit);
|
||||||
|
|
||||||
NotificationCompat.WearableExtender wearableExtender =
|
Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||||
new NotificationCompat.WearableExtender().setBackground(
|
|
||||||
BitmapFactory.decodeResource(context.getResources(), R.drawable.stripe));
|
|
||||||
|
|
||||||
Notification notification =
|
NotificationCompat.WearableExtender wearableExtender =
|
||||||
new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_notification)
|
new NotificationCompat.WearableExtender().setBackground(
|
||||||
.setContentTitle(habit.name)
|
BitmapFactory.decodeResource(context.getResources(), R.drawable.stripe));
|
||||||
.setContentText(habit.description)
|
|
||||||
.setContentIntent(contentPendingIntent)
|
|
||||||
.setDeleteIntent(dismissPendingIntent)
|
|
||||||
.addAction(R.drawable.ic_action_check,
|
|
||||||
context.getString(R.string.check), checkIntentPending)
|
|
||||||
.addAction(R.drawable.ic_action_snooze,
|
|
||||||
context.getString(R.string.snooze), snoozeIntentPending)
|
|
||||||
.setSound(soundUri)
|
|
||||||
.extend(wearableExtender)
|
|
||||||
.setWhen(reminderTime)
|
|
||||||
.setShowWhen(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
notification.flags |= Notification.FLAG_AUTO_CANCEL;
|
Notification notification =
|
||||||
|
new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_notification)
|
||||||
|
.setContentTitle(habit.name)
|
||||||
|
.setContentText(habit.description)
|
||||||
|
.setContentIntent(contentPendingIntent)
|
||||||
|
.setDeleteIntent(dismissPendingIntent)
|
||||||
|
.addAction(R.drawable.ic_action_check,
|
||||||
|
context.getString(R.string.check), checkIntentPending)
|
||||||
|
.addAction(R.drawable.ic_action_snooze,
|
||||||
|
context.getString(R.string.snooze), snoozeIntentPending)
|
||||||
|
.setSound(soundUri)
|
||||||
|
.extend(wearableExtender)
|
||||||
|
.setWhen(reminderTime)
|
||||||
|
.setShowWhen(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
NotificationManager notificationManager =
|
notification.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||||
(NotificationManager) context.getSystemService(Activity.NOTIFICATION_SERVICE);
|
|
||||||
|
|
||||||
int notificationId = (int) (habit.getId() % Integer.MAX_VALUE);
|
NotificationManager notificationManager =
|
||||||
notificationManager.notify(notificationId, notification);
|
(NotificationManager) context.getSystemService(Activity.NOTIFICATION_SERVICE);
|
||||||
|
|
||||||
|
int notificationId = (int) (habit.getId() % Integer.MAX_VALUE);
|
||||||
|
notificationManager.notify(notificationId, notification);
|
||||||
|
|
||||||
|
super.onPostExecute(aVoid);
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PendingIntent buildSnoozeIntent(Context context, Habit habit)
|
public static PendingIntent buildSnoozeIntent(Context context, Habit habit)
|
||||||
@@ -225,6 +237,13 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
|||||||
return PendingIntent.getBroadcast(context, 0, deleteIntent, 0);
|
return PendingIntent.getBroadcast(context, 0, deleteIntent, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PendingIntent buildViewHabitIntent(Context context, Habit habit)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent(context, ShowHabitActivity.class);
|
||||||
|
intent.setData(Uri.parse("content://org.isoron.uhabits/habit/" + habit.getId()));
|
||||||
|
return PendingIntent.getActivity(context, 0, intent, 0);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkWeekday(Intent intent, Habit habit)
|
private boolean checkWeekday(Intent intent, Habit habit)
|
||||||
{
|
{
|
||||||
Long timestamp = intent.getLongExtra("timestamp", DateHelper.getStartOfToday());
|
Long timestamp = intent.getLongExtra("timestamp", DateHelper.getStartOfToday());
|
||||||
|
|||||||
151
app/src/main/java/org/isoron/uhabits/HabitsApplication.java
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
import com.activeandroid.ActiveAndroid;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
public class HabitsApplication extends Application
|
||||||
|
{
|
||||||
|
@Nullable
|
||||||
|
private static Context context;
|
||||||
|
|
||||||
|
public static boolean isTestMode()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(context != null)
|
||||||
|
context.getClassLoader().loadClass("org.isoron.uhabits.unit.models.HabitTest");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (final Exception e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Context getContext()
|
||||||
|
{
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate()
|
||||||
|
{
|
||||||
|
super.onCreate();
|
||||||
|
HabitsApplication.context = this;
|
||||||
|
|
||||||
|
if (isTestMode())
|
||||||
|
{
|
||||||
|
File db = DatabaseHelper.getDatabaseFile();
|
||||||
|
if(db.exists()) db.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseHelper.initializeActiveAndroid();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTerminate()
|
||||||
|
{
|
||||||
|
HabitsApplication.context = null;
|
||||||
|
ActiveAndroid.dispose();
|
||||||
|
super.onTerminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLogcat() throws IOException
|
||||||
|
{
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
String[] command = new String[] { "logcat", "-d"};
|
||||||
|
Process process = Runtime.getRuntime().exec(command);
|
||||||
|
|
||||||
|
InputStreamReader in = new InputStreamReader(process.getInputStream());
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(in);
|
||||||
|
|
||||||
|
String line;
|
||||||
|
while ((line = bufferedReader.readLine()) != null)
|
||||||
|
{
|
||||||
|
builder.append(line);
|
||||||
|
builder.append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDeviceInfo()
|
||||||
|
{
|
||||||
|
if(context == null) return "";
|
||||||
|
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||||
|
|
||||||
|
b.append(String.format("App Version Name: %s\n", BuildConfig.VERSION_NAME));
|
||||||
|
b.append(String.format("App Version Code: %s\n", BuildConfig.VERSION_CODE));
|
||||||
|
b.append(String.format("OS Version: %s (%s)\n", System.getProperty("os.version"),
|
||||||
|
android.os.Build.VERSION.INCREMENTAL));
|
||||||
|
b.append(String.format("OS API Level: %s\n", android.os.Build.VERSION.SDK));
|
||||||
|
b.append(String.format("Device: %s\n", android.os.Build.DEVICE));
|
||||||
|
b.append(String.format("Model (Product): %s (%s)\n", android.os.Build.MODEL,
|
||||||
|
android.os.Build.PRODUCT));
|
||||||
|
b.append(String.format("Manufacturer: %s\n", android.os.Build.MANUFACTURER));
|
||||||
|
b.append(String.format("Other tags: %s\n", android.os.Build.TAGS));
|
||||||
|
b.append(String.format("Screen Width: %s\n", wm.getDefaultDisplay().getWidth()));
|
||||||
|
b.append(String.format("Screen Height: %s\n", wm.getDefaultDisplay().getHeight()));
|
||||||
|
b.append(String.format("SD Card state: %s\n\n", Environment.getExternalStorageState()));
|
||||||
|
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static File generateLogFile() throws IOException
|
||||||
|
{
|
||||||
|
String logcat = getLogcat();
|
||||||
|
String deviceInfo = getDeviceInfo();
|
||||||
|
String date = DateHelper.getBackupDateFormat().format(DateHelper.getLocalTime());
|
||||||
|
|
||||||
|
if(context == null) throw new RuntimeException("application context should not be null");
|
||||||
|
File dir = DatabaseHelper.getFilesDir("Logs");
|
||||||
|
if (dir == null) throw new IOException("log dir should not be null");
|
||||||
|
|
||||||
|
File logFile = new File(String.format("%s/Log %s.txt", dir.getPath(), date));
|
||||||
|
FileWriter output = new FileWriter(logFile);
|
||||||
|
output.write(deviceInfo);
|
||||||
|
output.write(logcat);
|
||||||
|
output.close();
|
||||||
|
|
||||||
|
return logFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,27 +26,32 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
import org.isoron.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
import org.isoron.helpers.DialogHelper;
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
import org.isoron.helpers.ReplayableActivity;
|
|
||||||
import org.isoron.uhabits.fragments.ListHabitsFragment;
|
import org.isoron.uhabits.fragments.ListHabitsFragment;
|
||||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.tasks.BaseTask;
|
||||||
import org.isoron.uhabits.widgets.CheckmarkWidgetProvider;
|
import org.isoron.uhabits.widgets.CheckmarkWidgetProvider;
|
||||||
import org.isoron.uhabits.widgets.FrequencyWidgetProvider;
|
import org.isoron.uhabits.widgets.FrequencyWidgetProvider;
|
||||||
import org.isoron.uhabits.widgets.HistoryWidgetProvider;
|
import org.isoron.uhabits.widgets.HistoryWidgetProvider;
|
||||||
import org.isoron.uhabits.widgets.ScoreWidgetProvider;
|
import org.isoron.uhabits.widgets.ScoreWidgetProvider;
|
||||||
import org.isoron.uhabits.widgets.StreakWidgetProvider;
|
import org.isoron.uhabits.widgets.StreakWidgetProvider;
|
||||||
|
|
||||||
public class MainActivity extends ReplayableActivity
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class MainActivity extends BaseActivity
|
||||||
implements ListHabitsFragment.OnHabitClickListener
|
implements ListHabitsFragment.OnHabitClickListener
|
||||||
{
|
{
|
||||||
private ListHabitsFragment listHabitsFragment;
|
private ListHabitsFragment listHabitsFragment;
|
||||||
@@ -56,6 +61,11 @@ public class MainActivity extends ReplayableActivity
|
|||||||
|
|
||||||
public static final String ACTION_REFRESH = "org.isoron.uhabits.ACTION_REFRESH";
|
public static final String ACTION_REFRESH = "org.isoron.uhabits.ACTION_REFRESH";
|
||||||
|
|
||||||
|
public static final int RESULT_IMPORT_DATA = 1;
|
||||||
|
public static final int RESULT_EXPORT_CSV = 2;
|
||||||
|
public static final int RESULT_EXPORT_DB = 3;
|
||||||
|
public static final int RESULT_BUG_REPORT = 4;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState)
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
@@ -76,8 +86,8 @@ public class MainActivity extends ReplayableActivity
|
|||||||
private void onStartup()
|
private void onStartup()
|
||||||
{
|
{
|
||||||
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
|
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
|
||||||
DialogHelper.incrementLaunchCount(this);
|
UIHelper.incrementLaunchCount(this);
|
||||||
DialogHelper.updateLastAppVersion(this);
|
UIHelper.updateLastAppVersion(this);
|
||||||
showTutorial();
|
showTutorial();
|
||||||
|
|
||||||
new AsyncTask<Void, Void, Void>() {
|
new AsyncTask<Void, Void, Void>() {
|
||||||
@@ -123,7 +133,7 @@ public class MainActivity extends ReplayableActivity
|
|||||||
case R.id.action_settings:
|
case R.id.action_settings:
|
||||||
{
|
{
|
||||||
Intent intent = new Intent(this, SettingsActivity.class);
|
Intent intent = new Intent(this, SettingsActivity.class);
|
||||||
startActivity(intent);
|
startActivityForResult(intent, 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,11 +144,62 @@ public class MainActivity extends ReplayableActivity
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case R.id.action_faq:
|
||||||
|
{
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(getString(R.string.helpURL)));
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||||
|
{
|
||||||
|
switch (resultCode)
|
||||||
|
{
|
||||||
|
case RESULT_IMPORT_DATA:
|
||||||
|
listHabitsFragment.showImportDialog();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RESULT_EXPORT_CSV:
|
||||||
|
listHabitsFragment.exportAllHabits();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RESULT_EXPORT_DB:
|
||||||
|
listHabitsFragment.exportDB();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RESULT_BUG_REPORT:
|
||||||
|
generateBugReport();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateBugReport()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File logFile = HabitsApplication.generateLogFile();
|
||||||
|
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_SENDTO);
|
||||||
|
intent.setData(Uri.parse(getString(R.string.bugReportURL)));
|
||||||
|
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(logFile));
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
showToast(R.string.bug_report_failed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onHabitClicked(Habit habit)
|
public void onHabitClicked(Habit habit)
|
||||||
{
|
{
|
||||||
@@ -152,15 +213,14 @@ public class MainActivity extends ReplayableActivity
|
|||||||
{
|
{
|
||||||
listHabitsFragment.onPostExecuteCommand(refreshKey);
|
listHabitsFragment.onPostExecuteCommand(refreshKey);
|
||||||
|
|
||||||
new AsyncTask<Void, Void, Void>()
|
new BaseTask()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... params)
|
protected void doInBackground()
|
||||||
{
|
{
|
||||||
updateWidgets(MainActivity.this);
|
updateWidgets(MainActivity.this);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
};
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateWidgets(Context context)
|
public static void updateWidgets(Context context)
|
||||||
@@ -197,4 +257,14 @@ public class MainActivity extends ReplayableActivity
|
|||||||
listHabitsFragment.onPostExecuteCommand(null);
|
listHabitsFragment.onPostExecuteCommand(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||||
|
@NonNull int[] grantResults)
|
||||||
|
{
|
||||||
|
if (grantResults.length <= 0) return;
|
||||||
|
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) return;
|
||||||
|
|
||||||
|
listHabitsFragment.showImportDialog();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,28 +20,16 @@
|
|||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
import android.app.ActionBar;
|
import android.app.ActionBar;
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
|
||||||
|
|
||||||
import org.isoron.helpers.ReplayableActivity;
|
|
||||||
import org.isoron.uhabits.fragments.ShowHabitFragment;
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
public class ShowHabitActivity extends ReplayableActivity
|
public class ShowHabitActivity extends BaseActivity
|
||||||
{
|
{
|
||||||
|
private Habit habit;
|
||||||
public Habit habit;
|
|
||||||
private Receiver receiver;
|
|
||||||
private LocalBroadcastManager localBroadcastManager;
|
|
||||||
|
|
||||||
private ShowHabitFragment fragment;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState)
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
@@ -52,37 +40,18 @@ public class ShowHabitActivity extends ReplayableActivity
|
|||||||
habit = Habit.get(ContentUris.parseId(data));
|
habit = Habit.get(ContentUris.parseId(data));
|
||||||
ActionBar actionBar = getActionBar();
|
ActionBar actionBar = getActionBar();
|
||||||
|
|
||||||
if(actionBar != null)
|
if(actionBar != null && getHabit() != null)
|
||||||
{
|
{
|
||||||
actionBar.setTitle(habit.name);
|
actionBar.setTitle(getHabit().name);
|
||||||
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
if (android.os.Build.VERSION.SDK_INT >= 21)
|
||||||
actionBar.setBackgroundDrawable(new ColorDrawable(habit.color));
|
actionBar.setBackgroundDrawable(new ColorDrawable(getHabit().color));
|
||||||
}
|
}
|
||||||
|
|
||||||
setContentView(R.layout.show_habit_activity);
|
setContentView(R.layout.show_habit_activity);
|
||||||
|
|
||||||
fragment = (ShowHabitFragment) getFragmentManager().findFragmentById(R.id.fragment2);
|
|
||||||
|
|
||||||
receiver = new Receiver();
|
|
||||||
localBroadcastManager = LocalBroadcastManager.getInstance(this);
|
|
||||||
localBroadcastManager.registerReceiver(receiver,
|
|
||||||
new IntentFilter(MainActivity.ACTION_REFRESH));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Receiver extends BroadcastReceiver
|
public Habit getHabit()
|
||||||
{
|
{
|
||||||
@Override
|
return habit;
|
||||||
public void onReceive(Context context, Intent intent)
|
|
||||||
{
|
|
||||||
fragment.refreshData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy()
|
|
||||||
{
|
|
||||||
localBroadcastManager.unregisterReceiver(receiver);
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,9 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.commands;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
import org.isoron.helpers.Command;
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ArchiveHabitsCommand extends Command
|
public class ArchiveHabitsCommand extends Command
|
||||||
@@ -31,12 +29,6 @@ public class ArchiveHabitsCommand extends Command
|
|||||||
|
|
||||||
private List<Habit> habits;
|
private List<Habit> habits;
|
||||||
|
|
||||||
public ArchiveHabitsCommand(Habit habit)
|
|
||||||
{
|
|
||||||
habits = new LinkedList<>();
|
|
||||||
habits.add(habit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArchiveHabitsCommand(List<Habit> habits)
|
public ArchiveHabitsCommand(List<Habit> habits)
|
||||||
{
|
{
|
||||||
this.habits = habits;
|
this.habits = habits;
|
||||||
@@ -45,15 +37,13 @@ public class ArchiveHabitsCommand extends Command
|
|||||||
@Override
|
@Override
|
||||||
public void execute()
|
public void execute()
|
||||||
{
|
{
|
||||||
for(Habit h : habits)
|
Habit.archive(habits);
|
||||||
h.archive();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void undo()
|
public void undo()
|
||||||
{
|
{
|
||||||
for(Habit h : habits)
|
Habit.unarchive(habits);
|
||||||
h.unarchive();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getExecuteStringId()
|
public Integer getExecuteStringId()
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ package org.isoron.uhabits.commands;
|
|||||||
|
|
||||||
import com.activeandroid.ActiveAndroid;
|
import com.activeandroid.ActiveAndroid;
|
||||||
|
|
||||||
import org.isoron.helpers.Command;
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -47,44 +47,25 @@ public class ChangeHabitColorCommand extends Command
|
|||||||
@Override
|
@Override
|
||||||
public void execute()
|
public void execute()
|
||||||
{
|
{
|
||||||
ActiveAndroid.beginTransaction();
|
Habit.setColor(habits, newColor);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for(Habit h : habits)
|
|
||||||
{
|
|
||||||
h.color = newColor;
|
|
||||||
h.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
ActiveAndroid.setTransactionSuccessful();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ActiveAndroid.endTransaction();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void undo()
|
public void undo()
|
||||||
{
|
{
|
||||||
ActiveAndroid.beginTransaction();
|
DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command()
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
int k = 0;
|
@Override
|
||||||
for(Habit h : habits)
|
public void execute()
|
||||||
{
|
{
|
||||||
h.color = originalColors.get(k++);
|
int k = 0;
|
||||||
h.save();
|
for(Habit h : habits)
|
||||||
|
{
|
||||||
|
h.color = originalColors.get(k++);
|
||||||
|
h.save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
ActiveAndroid.setTransactionSuccessful();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ActiveAndroid.endTransaction();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getExecuteStringId()
|
public Integer getExecuteStringId()
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.helpers;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
public abstract class Command
|
public abstract class Command
|
||||||
{
|
{
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.commands;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
import org.isoron.helpers.Command;
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
@@ -51,7 +50,10 @@ public class CreateHabitCommand extends Command
|
|||||||
@Override
|
@Override
|
||||||
public void undo()
|
public void undo()
|
||||||
{
|
{
|
||||||
Habit.get(savedId).delete();
|
Habit habit = Habit.get(savedId);
|
||||||
|
if(habit == null) throw new RuntimeException("Habit not found");
|
||||||
|
|
||||||
|
habit.cascadeDelete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.commands;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
import org.isoron.helpers.Command;
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.commands;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
import org.isoron.helpers.Command;
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
@@ -40,29 +39,36 @@ public class EditHabitCommand extends Command
|
|||||||
!this.original.freqNum.equals(this.modified.freqNum));
|
!this.original.freqNum.equals(this.modified.freqNum));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void execute()
|
public void execute()
|
||||||
{
|
{
|
||||||
Habit habit = Habit.get(savedId);
|
copyAttributes(this.modified);
|
||||||
habit.copyAttributes(modified);
|
|
||||||
habit.save();
|
|
||||||
if (hasIntervalChanged)
|
|
||||||
{
|
|
||||||
habit.checkmarks.deleteNewerThan(0);
|
|
||||||
habit.streaks.deleteNewerThan(0);
|
|
||||||
habit.scores.deleteNewerThan(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void undo()
|
public void undo()
|
||||||
|
{
|
||||||
|
copyAttributes(this.original);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyAttributes(Habit model)
|
||||||
{
|
{
|
||||||
Habit habit = Habit.get(savedId);
|
Habit habit = Habit.get(savedId);
|
||||||
habit.copyAttributes(original);
|
if(habit == null) throw new RuntimeException("Habit not found");
|
||||||
|
|
||||||
|
habit.copyAttributes(model);
|
||||||
habit.save();
|
habit.save();
|
||||||
|
|
||||||
|
invalidateIfNeeded(habit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidateIfNeeded(Habit habit)
|
||||||
|
{
|
||||||
if (hasIntervalChanged)
|
if (hasIntervalChanged)
|
||||||
{
|
{
|
||||||
habit.checkmarks.deleteNewerThan(0);
|
habit.checkmarks.deleteNewerThan(0);
|
||||||
habit.streaks.deleteNewerThan(0);
|
habit.streaks.deleteNewerThan(0);
|
||||||
habit.scores.deleteNewerThan(0);
|
habit.scores.invalidateNewerThan(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.commands;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
import org.isoron.helpers.Command;
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
public class ToggleRepetitionCommand extends Command
|
public class ToggleRepetitionCommand extends Command
|
||||||
|
|||||||
@@ -19,11 +19,9 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.commands;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
import org.isoron.helpers.Command;
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class UnarchiveHabitsCommand extends Command
|
public class UnarchiveHabitsCommand extends Command
|
||||||
@@ -31,12 +29,6 @@ public class UnarchiveHabitsCommand extends Command
|
|||||||
|
|
||||||
private List<Habit> habits;
|
private List<Habit> habits;
|
||||||
|
|
||||||
public UnarchiveHabitsCommand(Habit habit)
|
|
||||||
{
|
|
||||||
habits = new LinkedList<>();
|
|
||||||
habits.add(habit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnarchiveHabitsCommand(List<Habit> habits)
|
public UnarchiveHabitsCommand(List<Habit> habits)
|
||||||
{
|
{
|
||||||
this.habits = habits;
|
this.habits = habits;
|
||||||
@@ -45,15 +37,13 @@ public class UnarchiveHabitsCommand extends Command
|
|||||||
@Override
|
@Override
|
||||||
public void execute()
|
public void execute()
|
||||||
{
|
{
|
||||||
for(Habit h : habits)
|
Habit.unarchive(habits);
|
||||||
h.unarchive();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void undo()
|
public void undo()
|
||||||
{
|
{
|
||||||
for(Habit h : habits)
|
Habit.archive(habits);
|
||||||
h.archive();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getExecuteStringId()
|
public Integer getExecuteStringId()
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.dialogs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowManager.LayoutParams;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class FilePickerDialog implements AdapterView.OnItemClickListener
|
||||||
|
{
|
||||||
|
private static final String PARENT_DIR = "..";
|
||||||
|
|
||||||
|
private final Activity activity;
|
||||||
|
private ListView list;
|
||||||
|
private Dialog dialog;
|
||||||
|
private File currentPath;
|
||||||
|
|
||||||
|
public interface OnFileSelectedListener
|
||||||
|
{
|
||||||
|
void onFileSelected(File file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OnFileSelectedListener listener;
|
||||||
|
|
||||||
|
public FilePickerDialog(Activity activity, File initialDirectory)
|
||||||
|
{
|
||||||
|
this.activity = activity;
|
||||||
|
|
||||||
|
list = new ListView(activity);
|
||||||
|
list.setOnItemClickListener(this);
|
||||||
|
|
||||||
|
dialog = new Dialog(activity);
|
||||||
|
dialog.setContentView(list);
|
||||||
|
dialog.getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||||
|
|
||||||
|
navigateTo(initialDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int which, long id)
|
||||||
|
{
|
||||||
|
String filename = (String) list.getItemAtPosition(which);
|
||||||
|
File file;
|
||||||
|
|
||||||
|
if (filename.equals(PARENT_DIR))
|
||||||
|
file = currentPath.getParentFile();
|
||||||
|
else
|
||||||
|
file = new File(currentPath, filename);
|
||||||
|
|
||||||
|
if (file.isDirectory())
|
||||||
|
{
|
||||||
|
navigateTo(file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (listener != null) listener.onFileSelected(file);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show()
|
||||||
|
{
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListener(OnFileSelectedListener listener)
|
||||||
|
{
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navigateTo(File path)
|
||||||
|
{
|
||||||
|
if (!path.exists()) return;
|
||||||
|
|
||||||
|
File[] dirs = path.listFiles(new ReadableDirFilter());
|
||||||
|
File[] files = path.listFiles(new RegularReadableFileFilter());
|
||||||
|
if(dirs == null || files == null) return;
|
||||||
|
|
||||||
|
this.currentPath = path;
|
||||||
|
dialog.setTitle(currentPath.getPath());
|
||||||
|
list.setAdapter(new FilePickerAdapter(getFileList(path, dirs, files)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String[] getFileList(File path, File[] dirs, File[] files)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
int length = dirs.length + files.length;
|
||||||
|
String[] fileList;
|
||||||
|
|
||||||
|
if (path.getParentFile() == null || !path.getParentFile().canRead())
|
||||||
|
{
|
||||||
|
fileList = new String[length];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fileList = new String[length + 1];
|
||||||
|
fileList[count++] = PARENT_DIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
Arrays.sort(dirs);
|
||||||
|
Arrays.sort(files);
|
||||||
|
|
||||||
|
for (File dir : dirs)
|
||||||
|
fileList[count++] = dir.getName();
|
||||||
|
|
||||||
|
for (File file : files)
|
||||||
|
fileList[count++] = file.getName();
|
||||||
|
|
||||||
|
return fileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FilePickerAdapter extends ArrayAdapter<String>
|
||||||
|
{
|
||||||
|
public FilePickerAdapter(@NonNull String[] fileList)
|
||||||
|
{
|
||||||
|
super(FilePickerDialog.this.activity, android.R.layout.simple_list_item_1, fileList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int pos, View view, ViewGroup parent)
|
||||||
|
{
|
||||||
|
view = super.getView(pos, view, parent);
|
||||||
|
TextView tv = (TextView) view;
|
||||||
|
tv.setSingleLine(true);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ReadableDirFilter implements FileFilter
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean accept(File file)
|
||||||
|
{
|
||||||
|
return (file.isDirectory() && file.canRead());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RegularReadableFileFilter implements FileFilter
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean accept(File file)
|
||||||
|
{
|
||||||
|
return !file.isDirectory() && file.canRead();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,7 @@ import android.util.Log;
|
|||||||
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.tasks.BaseTask;
|
||||||
import org.isoron.uhabits.views.HabitHistoryView;
|
import org.isoron.uhabits.views.HabitHistoryView;
|
||||||
|
|
||||||
public class HistoryEditorDialog extends DialogFragment
|
public class HistoryEditorDialog extends DialogFragment
|
||||||
@@ -44,7 +45,6 @@ public class HistoryEditorDialog extends DialogFragment
|
|||||||
{
|
{
|
||||||
Context context = getActivity();
|
Context context = getActivity();
|
||||||
historyView = new HabitHistoryView(context, null);
|
historyView = new HabitHistoryView(context, null);
|
||||||
int p = (int) getResources().getDimension(R.dimen.history_editor_padding);
|
|
||||||
|
|
||||||
if(savedInstanceState != null)
|
if(savedInstanceState != null)
|
||||||
{
|
{
|
||||||
@@ -52,7 +52,8 @@ public class HistoryEditorDialog extends DialogFragment
|
|||||||
if(id > 0) this.habit = Habit.get(id);
|
if(id > 0) this.habit = Habit.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
historyView.setPadding(p, 0, p, 0);
|
int padding = (int) getResources().getDimension(R.dimen.history_editor_padding);
|
||||||
|
historyView.setPadding(padding, 0, padding, 0);
|
||||||
historyView.setHabit(habit);
|
historyView.setHabit(habit);
|
||||||
historyView.setIsEditable(true);
|
historyView.setIsEditable(true);
|
||||||
|
|
||||||
@@ -61,9 +62,23 @@ public class HistoryEditorDialog extends DialogFragment
|
|||||||
.setView(historyView)
|
.setView(historyView)
|
||||||
.setPositiveButton(android.R.string.ok, this);
|
.setPositiveButton(android.R.string.ok, this);
|
||||||
|
|
||||||
|
refreshData();
|
||||||
|
|
||||||
return builder.create();
|
return builder.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void refreshData()
|
||||||
|
{
|
||||||
|
new BaseTask()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected void doInBackground()
|
||||||
|
{
|
||||||
|
historyView.refreshData();
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume()
|
public void onResume()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import android.app.DialogFragment;
|
|||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import org.isoron.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
|
|
||||||
public class WeekdayPickerDialog extends DialogFragment
|
public class WeekdayPickerDialog extends DialogFragment
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.fragments;
|
package org.isoron.uhabits.fragments;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.DialogFragment;
|
import android.app.DialogFragment;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
@@ -29,8 +29,10 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.android.colorpicker.ColorPickerDialog;
|
import com.android.colorpicker.ColorPickerDialog;
|
||||||
@@ -38,11 +40,11 @@ import com.android.colorpicker.ColorPickerSwatch;
|
|||||||
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.helpers.ColorHelper;
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
import org.isoron.helpers.Command;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
import org.isoron.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.UIHelper.OnSavedListener;
|
||||||
import org.isoron.helpers.DialogHelper.OnSavedListener;
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.commands.Command;
|
||||||
import org.isoron.uhabits.commands.CreateHabitCommand;
|
import org.isoron.uhabits.commands.CreateHabitCommand;
|
||||||
import org.isoron.uhabits.commands.EditHabitCommand;
|
import org.isoron.uhabits.commands.EditHabitCommand;
|
||||||
import org.isoron.uhabits.dialogs.WeekdayPickerDialog;
|
import org.isoron.uhabits.dialogs.WeekdayPickerDialog;
|
||||||
@@ -52,7 +54,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
public class EditHabitFragment extends DialogFragment
|
public class EditHabitFragment extends DialogFragment
|
||||||
implements OnClickListener, WeekdayPickerDialog.OnWeekdaysPickedListener,
|
implements OnClickListener, WeekdayPickerDialog.OnWeekdaysPickedListener,
|
||||||
TimePickerDialog.OnTimeSetListener
|
TimePickerDialog.OnTimeSetListener, Spinner.OnItemSelectedListener
|
||||||
{
|
{
|
||||||
private Integer mode;
|
private Integer mode;
|
||||||
static final int EDIT_MODE = 0;
|
static final int EDIT_MODE = 0;
|
||||||
@@ -70,6 +72,10 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
private TextView tvReminderTime;
|
private TextView tvReminderTime;
|
||||||
private TextView tvReminderDays;
|
private TextView tvReminderDays;
|
||||||
|
|
||||||
|
private Spinner sFrequency;
|
||||||
|
private ViewGroup llCustomFrequency;
|
||||||
|
private ViewGroup llReminderDays;
|
||||||
|
|
||||||
private SharedPreferences prefs;
|
private SharedPreferences prefs;
|
||||||
private boolean is24HourMode;
|
private boolean is24HourMode;
|
||||||
|
|
||||||
@@ -104,6 +110,10 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
tvReminderTime = (TextView) view.findViewById(R.id.inputReminderTime);
|
tvReminderTime = (TextView) view.findViewById(R.id.inputReminderTime);
|
||||||
tvReminderDays = (TextView) view.findViewById(R.id.inputReminderDays);
|
tvReminderDays = (TextView) view.findViewById(R.id.inputReminderDays);
|
||||||
|
|
||||||
|
sFrequency = (Spinner) view.findViewById(R.id.sFrequency);
|
||||||
|
llCustomFrequency = (ViewGroup) view.findViewById(R.id.llCustomFrequency);
|
||||||
|
llReminderDays = (ViewGroup) view.findViewById(R.id.llReminderDays);
|
||||||
|
|
||||||
Button buttonSave = (Button) view.findViewById(R.id.buttonSave);
|
Button buttonSave = (Button) view.findViewById(R.id.buttonSave);
|
||||||
Button buttonDiscard = (Button) view.findViewById(R.id.buttonDiscard);
|
Button buttonDiscard = (Button) view.findViewById(R.id.buttonDiscard);
|
||||||
ImageButton buttonPickColor = (ImageButton) view.findViewById(R.id.buttonPickColor);
|
ImageButton buttonPickColor = (ImageButton) view.findViewById(R.id.buttonPickColor);
|
||||||
@@ -113,6 +123,7 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
tvReminderTime.setOnClickListener(this);
|
tvReminderTime.setOnClickListener(this);
|
||||||
tvReminderDays.setOnClickListener(this);
|
tvReminderDays.setOnClickListener(this);
|
||||||
buttonPickColor.setOnClickListener(this);
|
buttonPickColor.setOnClickListener(this);
|
||||||
|
sFrequency.setOnItemSelectedListener(this);
|
||||||
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||||
|
|
||||||
@@ -125,18 +136,16 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
{
|
{
|
||||||
getDialog().setTitle(R.string.create_habit);
|
getDialog().setTitle(R.string.create_habit);
|
||||||
modifiedHabit = new Habit();
|
modifiedHabit = new Habit();
|
||||||
|
modifiedHabit.freqNum = 1;
|
||||||
int defaultNum = prefs.getInt("pref_default_habit_freq_num", modifiedHabit.freqNum);
|
modifiedHabit.freqDen = 1;
|
||||||
int defaultDen = prefs.getInt("pref_default_habit_freq_den", modifiedHabit.freqDen);
|
modifiedHabit.color = prefs.getInt("pref_default_habit_color", modifiedHabit.color);
|
||||||
int defaultColor = prefs.getInt("pref_default_habit_color", modifiedHabit.color);
|
|
||||||
|
|
||||||
modifiedHabit.color = defaultColor;
|
|
||||||
modifiedHabit.freqNum = defaultNum;
|
|
||||||
modifiedHabit.freqDen = defaultDen;
|
|
||||||
}
|
}
|
||||||
else if (mode == EDIT_MODE)
|
else if (mode == EDIT_MODE)
|
||||||
{
|
{
|
||||||
originalHabit = Habit.get((Long) args.get("habitId"));
|
Long habitId = (Long) args.get("habitId");
|
||||||
|
if(habitId == null) throw new IllegalArgumentException("habitId must be specified");
|
||||||
|
|
||||||
|
originalHabit = Habit.get(habitId);
|
||||||
modifiedHabit = new Habit(originalHabit);
|
modifiedHabit = new Habit(originalHabit);
|
||||||
|
|
||||||
getDialog().setTitle(R.string.edit_habit);
|
getDialog().setTitle(R.string.edit_habit);
|
||||||
@@ -152,17 +161,14 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
modifiedHabit.reminderDays = savedInstanceState.getInt("reminderDays", -1);
|
modifiedHabit.reminderDays = savedInstanceState.getInt("reminderDays", -1);
|
||||||
|
|
||||||
if(modifiedHabit.reminderMin < 0)
|
if(modifiedHabit.reminderMin < 0)
|
||||||
{
|
modifiedHabit.clearReminder();
|
||||||
modifiedHabit.reminderMin = null;
|
|
||||||
modifiedHabit.reminderHour = null;
|
|
||||||
modifiedHabit.reminderDays = 127;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tvFreqNum.append(modifiedHabit.freqNum.toString());
|
tvFreqNum.append(modifiedHabit.freqNum.toString());
|
||||||
tvFreqDen.append(modifiedHabit.freqDen.toString());
|
tvFreqDen.append(modifiedHabit.freqDen.toString());
|
||||||
|
|
||||||
changeColor(modifiedHabit.color);
|
changeColor(modifiedHabit.color);
|
||||||
|
updateFrequency();
|
||||||
updateReminder();
|
updateReminder();
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
@@ -178,24 +184,23 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
private void updateReminder()
|
private void updateReminder()
|
||||||
{
|
{
|
||||||
if (modifiedHabit.reminderHour != null)
|
if (modifiedHabit.hasReminder())
|
||||||
{
|
{
|
||||||
tvReminderTime.setTextColor(Color.BLACK);
|
|
||||||
tvReminderTime.setText(DateHelper.formatTime(getActivity(), modifiedHabit.reminderHour,
|
tvReminderTime.setText(DateHelper.formatTime(getActivity(), modifiedHabit.reminderHour,
|
||||||
modifiedHabit.reminderMin));
|
modifiedHabit.reminderMin));
|
||||||
tvReminderDays.setVisibility(View.VISIBLE);
|
llReminderDays.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
boolean weekdays[] = DateHelper.unpackWeekdayList(modifiedHabit.reminderDays);
|
||||||
|
tvReminderDays.setText(DateHelper.formatWeekdayList(getActivity(), weekdays));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tvReminderTime.setTextColor(Color.GRAY);
|
|
||||||
tvReminderTime.setText(R.string.reminder_off);
|
tvReminderTime.setText(R.string.reminder_off);
|
||||||
tvReminderDays.setVisibility(View.GONE);
|
llReminderDays.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean weekdays[] = DateHelper.unpackWeekdayList(modifiedHabit.reminderDays);
|
|
||||||
tvReminderDays.setText(DateHelper.formatWeekdayList(getActivity(), weekdays));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnSavedListener(OnSavedListener onSavedListener)
|
public void setOnSavedListener(OnSavedListener onSavedListener)
|
||||||
@@ -257,11 +262,6 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
|
|
||||||
if (!validate()) return;
|
if (!validate()) return;
|
||||||
|
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
|
||||||
editor.putInt("pref_default_habit_freq_num", modifiedHabit.freqNum);
|
|
||||||
editor.putInt("pref_default_habit_freq_den", modifiedHabit.freqDen);
|
|
||||||
editor.apply();
|
|
||||||
|
|
||||||
Command command = null;
|
Command command = null;
|
||||||
Habit savedHabit = null;
|
Habit savedHabit = null;
|
||||||
|
|
||||||
@@ -305,12 +305,13 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
private void onDateSpinnerClick()
|
private void onDateSpinnerClick()
|
||||||
{
|
{
|
||||||
int defaultHour = 8;
|
int defaultHour = 8;
|
||||||
int defaultMin = 0;
|
int defaultMin = 0;
|
||||||
|
|
||||||
if (modifiedHabit.reminderHour != null)
|
if (modifiedHabit.hasReminder())
|
||||||
{
|
{
|
||||||
defaultHour = modifiedHabit.reminderHour;
|
defaultHour = modifiedHabit.reminderHour;
|
||||||
defaultMin = modifiedHabit.reminderMin;
|
defaultMin = modifiedHabit.reminderMin;
|
||||||
@@ -321,8 +322,11 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
timePicker.show(getFragmentManager(), "timePicker");
|
timePicker.show(getFragmentManager(), "timePicker");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
private void onWeekdayClick()
|
private void onWeekdayClick()
|
||||||
{
|
{
|
||||||
|
if(!modifiedHabit.hasReminder()) return;
|
||||||
|
|
||||||
WeekdayPickerDialog dialog = new WeekdayPickerDialog();
|
WeekdayPickerDialog dialog = new WeekdayPickerDialog();
|
||||||
dialog.setListener(this);
|
dialog.setListener(this);
|
||||||
dialog.setSelectedDays(DateHelper.unpackWeekdayList(modifiedHabit.reminderDays));
|
dialog.setSelectedDays(DateHelper.unpackWeekdayList(modifiedHabit.reminderDays));
|
||||||
@@ -334,14 +338,14 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
{
|
{
|
||||||
modifiedHabit.reminderHour = hour;
|
modifiedHabit.reminderHour = hour;
|
||||||
modifiedHabit.reminderMin = minute;
|
modifiedHabit.reminderMin = minute;
|
||||||
|
modifiedHabit.reminderDays = DateHelper.ALL_WEEK_DAYS;
|
||||||
updateReminder();
|
updateReminder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimeCleared(RadialPickerLayout view)
|
public void onTimeCleared(RadialPickerLayout view)
|
||||||
{
|
{
|
||||||
modifiedHabit.reminderHour = null;
|
modifiedHabit.clearReminder();
|
||||||
modifiedHabit.reminderMin = null;
|
|
||||||
updateReminder();
|
updateReminder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,16 +363,93 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
public void onSaveInstanceState(Bundle outState)
|
public void onSaveInstanceState(Bundle outState)
|
||||||
{
|
{
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
|
|
||||||
outState.putInt("color", modifiedHabit.color);
|
outState.putInt("color", modifiedHabit.color);
|
||||||
if(modifiedHabit.reminderHour != null)
|
|
||||||
|
if(modifiedHabit.hasReminder())
|
||||||
{
|
{
|
||||||
outState.putInt("reminderMin", modifiedHabit.reminderMin);
|
outState.putInt("reminderMin", modifiedHabit.reminderMin);
|
||||||
outState.putInt("reminderHour", modifiedHabit.reminderHour);
|
outState.putInt("reminderHour", modifiedHabit.reminderHour);
|
||||||
outState.putInt("reminderDays", modifiedHabit.reminderDays);
|
outState.putInt("reminderDays", modifiedHabit.reminderDays);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
|
||||||
|
{
|
||||||
|
if(parent.getId() == R.id.sFrequency)
|
||||||
|
{
|
||||||
|
switch (position)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
modifiedHabit.freqNum = 1;
|
||||||
|
modifiedHabit.freqDen = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
modifiedHabit.freqNum = 1;
|
||||||
|
modifiedHabit.freqDen = 7;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
modifiedHabit.freqNum = 2;
|
||||||
|
modifiedHabit.freqDen = 7;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
modifiedHabit.freqNum = 5;
|
||||||
|
modifiedHabit.freqDen = 7;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
modifiedHabit.freqNum = 3;
|
||||||
|
modifiedHabit.freqDen = 7;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
private void updateFrequency()
|
||||||
|
{
|
||||||
|
int quickSelectPosition = -1;
|
||||||
|
|
||||||
|
if(modifiedHabit.freqNum.equals(modifiedHabit.freqDen))
|
||||||
|
quickSelectPosition = 0;
|
||||||
|
|
||||||
|
else if(modifiedHabit.freqNum == 1 && modifiedHabit.freqDen == 7)
|
||||||
|
quickSelectPosition = 1;
|
||||||
|
|
||||||
|
else if(modifiedHabit.freqNum == 2 && modifiedHabit.freqDen == 7)
|
||||||
|
quickSelectPosition = 2;
|
||||||
|
|
||||||
|
else if(modifiedHabit.freqNum == 5 && modifiedHabit.freqDen == 7)
|
||||||
|
quickSelectPosition = 3;
|
||||||
|
|
||||||
|
if(quickSelectPosition >= 0)
|
||||||
|
{
|
||||||
|
sFrequency.setVisibility(View.VISIBLE);
|
||||||
|
sFrequency.setSelection(quickSelectPosition);
|
||||||
|
llCustomFrequency.setVisibility(View.GONE);
|
||||||
|
tvFreqNum.setText(modifiedHabit.freqNum.toString());
|
||||||
|
tvFreqDen.setText(modifiedHabit.freqDen.toString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sFrequency.setVisibility(View.GONE);
|
||||||
|
llCustomFrequency.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import android.widget.BaseAdapter;
|
|||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.isoron.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.helpers.ListHabitsHelper;
|
import org.isoron.uhabits.helpers.ListHabitsHelper;
|
||||||
import org.isoron.uhabits.loaders.HabitListLoader;
|
import org.isoron.uhabits.loaders.HabitListLoader;
|
||||||
|
|||||||