Compare commits

..

772 Commits

Author SHA1 Message Date
5e00d07b73 Merge branch 'hotfix/1.7.2' 2017-05-28 00:41:37 -04:00
28b6ae7014 Apply dark theme on dialogs
Fixes #291
2017-05-28 00:31:48 -04:00
2a1bf5fc2e Update CHANGELOG 2017-05-27 23:38:58 -04:00
ef7483f9dc Make SQLiteHabitList.toList synchronized 2017-05-27 23:37:24 -04:00
bbb9ed8f99 Update translations 2017-05-27 23:30:45 -04:00
c49d576871 Fix crash at startup 2017-05-27 23:29:35 -04:00
bc66ae4f7a Bump version to 1.7.2 2017-05-27 23:28:16 -04:00
fa416adbb9 Merge branch 'hotfix/1.7.1' 2017-05-21 10:20:48 -04:00
8b835b9918 Update CHANGELOG 2017-05-21 10:10:30 -04:00
471c5d341f Update translations 2017-05-21 10:03:13 -04:00
57296745b3 Improve performance of CheckmarkButtonView 2017-04-19 14:33:59 -04:00
140ab34a76 Verify database version before importing 2017-04-11 22:03:16 -04:00
0d6ad26505 Ignore exception when habit is not found 2017-04-11 21:19:03 -04:00
65cc99dbf7 Switch from View.BaseSavedState to Support Library's AbsSavedState
See https://code.google.com/p/android/issues/detail?id=196430
2017-04-11 17:49:55 -04:00
6855ef9d5e Update translations 2017-04-11 17:26:06 -04:00
4c58b084c6 Fix missing dialog title 2017-04-11 17:19:18 -04:00
5c8e522646 Fix header labels for RTL languages such as Arabic 2017-04-11 17:14:25 -04:00
55da0759d4 Bump version 2017-04-11 16:58:35 -04:00
692fe3218f Merge branch 'release/1.7.0' 2017-04-09 18:13:52 -04:00
387930c08d Fix icon size, remove unused icons 2017-04-09 17:37:14 -04:00
6bd31f9607 Add Basque, Catalan, Persian and Romanian languages 2017-04-09 17:16:08 -04:00
9aafe7160c Update translation links 2017-04-09 16:56:04 -04:00
5cc4aac67a Update translations 2017-04-09 16:41:32 -04:00
831421bc98 Ignore CrowdIn configuration files 2017-04-09 16:03:00 -04:00
161d8f2517 Update changelog 2017-03-31 09:12:53 -04:00
bfe4b822b3 Bump version 2017-03-31 08:15:04 -04:00
19e79a8559 Fix failing test 2017-03-20 22:32:53 -04:00
876d4f0ac7 Merge branch 'feature/scroll-header' into dev
Fixes #155
2017-03-20 20:05:26 -04:00
f4f7faf3a4 Fix a few issues with header scrolling 2017-03-20 20:00:10 -04:00
56f2ae57fe Persist scrolling after configuration change 2017-03-20 18:53:48 -04:00
3fe09efe9b Scroll checkmarks 2017-03-19 20:36:16 -04:00
f0de29fbfe Make HeaderView scrollable 2017-03-19 18:31:43 -04:00
324facfffd Re-enable haptic feedback on CheckmarkButtonView 2017-03-19 16:35:57 -04:00
03e58f9ef2 Preserve position of ScrollableChart on configuration changes
Fixes #240
2017-03-18 23:57:54 -04:00
e6c9f7f0c9 Preserve ScrollView position after configuration change
Fixes #239
2017-03-18 22:41:32 -04:00
42bdedb86a Update dependencies 2017-03-18 22:40:38 -04:00
ab0c510fda Rename export action and move to overflow menu 2017-03-18 22:22:07 -04:00
e46fd58664 Fix ImportTest on Nougat 2017-03-17 22:49:52 -04:00
8532bd402e Update APK filename 2017-03-17 22:11:48 -04:00
2c599b18ef Fix ambiguous reference to R 2017-03-17 21:52:51 -04:00
0d78ba4ba9 Update gradle 2017-03-17 21:52:13 -04:00
Janet Do
611dfa00a5 Export data from the statistics screen
Closes #27
2017-02-02 21:31:37 -05:00
Anirudha Agashe
54a195243d Fixed failing build
Replaced static method to obtain context with appropriate dependency
2017-01-18 12:58:56 -06:00
Anirudha Agashe
4fc30fae53 Remove use of static context from HabitsApplication class (#224)
Remove static context from application class. Replace static method to obtain context with appropriate dependency. Remove context from controller. Add auto factory to export db task.
2017-01-09 23:09:05 -05:00
b3fe9c65d2 Refresh data at midnight
Fixes #221
2016-12-31 18:24:26 -05:00
09f1ae8765 Update circle.yml 2016-12-31 12:30:10 -05:00
0a8b763ece Make TimePicker slightly smaller
Fixes #219
2016-12-30 12:58:37 -05:00
edd5f25529 Update circle.yml 2016-12-25 23:56:34 -05:00
d81fdb41dc Remove temp file after importing 2016-12-25 23:50:38 -05:00
02c8810e46 Bump targetSdkVersion to 25 2016-12-25 23:05:47 -05:00
6adf8061d3 Use FileProvider instead of File URIs 2016-12-25 23:05:17 -05:00
d19d57e5df Use Storage Access Framework when importing files 2016-12-25 11:54:08 -05:00
fd82e6c24b Remember last used order 2016-11-24 06:00:02 -05:00
56263efa39 Sort by score 2016-11-24 05:45:08 -05:00
d5eacba303 Disable drag-and-drop when automatically sorting 2016-11-24 05:27:26 -05:00
222261c674 Add order menu 2016-11-23 06:35:07 -05:00
b1a06df7f8 Implement automatic sorting for SQLHabitList 2016-11-23 05:58:46 -05:00
a1fc7dd0d1 Implement automatic sorting for MemoryHabitList 2016-11-04 05:59:34 -04:00
Justin Inácio
10131d5124 Allow intervals larger than 99 days (#202) 2016-11-02 06:13:31 -04:00
aa94959ad2 Merge tag 'v1.6.2' into dev
v1.6.2
2016-10-13 07:09:19 -04:00
967dc2586b Merge branch 'hotfix/1.6.2' 2016-10-13 07:09:11 -04:00
908fd1d6ae Fix NoSuchMethodError on Android 4.1 2016-10-11 06:34:22 -04:00
45fd8a29e1 Merge tag 'v1.6.1' into dev
v1.6.1
2016-10-10 12:10:23 -04:00
4624acd477 Merge branch 'hotfix/1.6.1' 2016-10-10 12:10:14 -04:00
c8cd4fa389 Delete old database cache 2016-10-10 11:39:18 -04:00
8c4fab28aa Merge tag 'v1.6.0' into dev
v1.6.0
2016-10-10 09:54:29 -04:00
d6b91cef01 Merge branch 'release/1.6.0' 2016-10-10 09:54:20 -04:00
e273fe7375 Automatically repair inconsistent databases before crashing 2016-10-10 08:49:16 -04:00
ab3b946c65 Update translations 2016-10-09 18:23:24 -04:00
38cd47c199 Remove empty title bar on TimePickerDialog 2016-10-09 13:18:36 -04:00
fa64bd389d Update list of developers 2016-10-09 12:47:33 -04:00
d03578fd9e Update changelog 2016-10-09 12:40:02 -04:00
79ae181df2 Bump version to 1.6.0 2016-10-09 12:14:03 -04:00
320dca9070 Automatically change order of days on pre-lollipop 2016-10-09 12:05:57 -04:00
f3b9fc825a HabitMatcher: only compute today's value if needed 2016-10-09 11:58:03 -04:00
50da14022f Fix bug that prevented ringtone from being saved 2016-10-09 11:47:30 -04:00
84a02fe541 Remove duplicate shadows and some duplicate code 2016-10-09 11:32:50 -04:00
b3aa3d14c0 Remove unused resources 2016-10-04 07:52:36 -04:00
e9cdfd23c4 Fix widgets on API 15 2016-10-04 07:43:40 -04:00
659ad2d817 Fix card background color (pre-lollipop) 2016-10-04 07:37:40 -04:00
d537ba0dfa Fix layout issues on pre-lollipop 2016-10-04 07:29:51 -04:00
7c14725d88 Always show menu icons 2016-10-04 06:57:01 -04:00
638b3f763c Fix some crashes on old devices 2016-09-30 08:10:25 -04:00
f876fc50bb Only recalculate all list positions when repairing database 2016-09-30 07:24:29 -04:00
919504ccfb Render widgets in a separate thread 2016-09-30 07:03:23 -04:00
1a8d9e72a1 Add Tasker screenshots 2016-09-25 11:55:16 -04:00
8e82f369c7 Implement Tasker/Locale plugin 2016-09-25 11:01:17 -04:00
0a5677211e Remove reference to MainActivity 2016-09-24 17:02:30 -04:00
65071797c9 SettingsActivity: Fix toolbar color in night mode 2016-09-24 16:49:45 -04:00
df1751b21a Refactor AboutActivity 2016-09-24 16:41:14 -04:00
4e952dd87a Remove MainActivity and use activity-alias 2016-09-24 16:03:41 -04:00
Nikhil
132dce8919 Improve error message on widget
Fixes #168
2016-09-19 21:24:32 -04:00
5c8450191a Make notifications actionable on the Pebble
Closes #126
2016-09-19 18:32:03 -04:00
b1d00598eb Add option to repair database 2016-09-18 08:09:18 -04:00
518ade3165 Add some database checks 2016-09-18 08:08:56 -04:00
dc5d7930a6 Merge pull request #173 from JotraN/dev
Add exporting scores and checkmarks files with multiple habits (#68).
2016-09-17 12:38:27 -04:00
jotran
ec34043041 Add exporting scores and checkmarks files with multiple habits (#68).
Add writeMultipleHabits to write a scores file and a checkmarks file
that contains scores and checkmarks of multiple habits.
Add getTimeframe because it was necessary to get the timeframe between
all habits so that row data could be populated correctly for habits that
started before/after other habits.
Move writeCheckmarks to below writeScores since it wasn't called until
after writeScores in writeHabits.

Add getByInterval to ScoreList, MemoryScoreList, and SQLiteScoreList to
get scores between a given interval - simiarly to CheckmarkList.

Add getValues (ScoreList) to get all values between a given timeframe.

Add getNewest to get the newest repetition in a list and to correspond
with getOldest.

Add getDaysBetween to DateUtils to quickly get the number of days
between two timestamps.

Add tests for the new functions.
2016-09-17 09:04:49 -07:00
4d5407a5cc Fix bug on compute methods that prevented them from recomputing old values 2016-09-16 13:17:52 -04:00
904489d812 Remove debug log 2016-09-16 11:41:20 -04:00
d2436a3165 Merge pull request #174 from Donaira/dev
Increase width of name column according to screen size
2016-09-14 15:35:21 -04:00
Donaira Tamulynaitė
bc19858bca Habit label takes up to 1/3 screen width. 2016-09-14 13:03:38 +03:00
a998a62cdb Remove notification when habit is deleted 2016-09-11 11:35:32 -04:00
1a89bb02be Add option to make notifications sticky 2016-09-11 10:40:43 -04:00
6e8ea471aa Do not cancel notifications automatically 2016-09-11 09:55:47 -04:00
ec42fb54f4 Do not store position of ViewHolder 2016-09-11 09:35:53 -04:00
c46fa84135 Fix bug that made it impossible to disable a reminder 2016-09-11 09:18:06 -04:00
16dcc0cbc2 Fix tests 2016-09-11 09:04:39 -04:00
6f10039aba Merge pull request #171 from regularcoder/dev
Add repetition count to overview card
2016-09-11 08:10:34 -04:00
RegularCoder
5cbc0a3292 Add repetition count to overview card, for #115 2016-09-05 21:14:12 -04:00
7f67a9eb63 Revert concurrent AsyncTasks 2016-09-03 09:37:16 -04:00
d3f7ebd60c SQLiteHabitList: fix corrupted order automatically 2016-09-03 09:33:09 -04:00
461fe1f0b6 Improve performance 2016-09-03 09:25:23 -04:00
8eb9f398d5 Allow multiple AsyncTasks to run concurrently
Fixes #134
2016-09-03 09:00:20 -04:00
f178bcbdd2 CheckmarkButtonView: avoid layout inflation 2016-09-03 08:00:36 -04:00
66c3136fad Allow views to be rendered by the layout editor 2016-09-03 07:46:41 -04:00
4b04966617 Update AMOLED mode upon exiting settings
Fixes #118
2016-09-02 22:24:21 -04:00
3ea21fe823 Update order of checkmark buttons automatically upon exiting settings 2016-09-02 21:13:56 -04:00
e0df69beb6 Prevent progress bar from flickering 2016-09-02 21:03:09 -04:00
b5cd4584b2 Disallow empty WeekdayLists 2016-09-02 21:02:38 -04:00
af7c8b1f2e Merge pull request #170 from regularcoder/dev
Fix issue#65 by setting launchMode to singleTop
2016-09-02 06:30:48 -04:00
RegularCoder
283cb2a3f0 Fix issue#65 by setting launchMode to singleTop 2016-09-01 21:43:28 -04:00
79ddbca307 Fix ripple when days are reversed 2016-08-29 20:57:43 -04:00
46a0777195 Restore progress bars 2016-08-29 20:53:48 -04:00
b35305e16c Preferences: update link to AboutActivity 2016-08-29 20:03:25 -04:00
8c4a745ecb Merge pull request #165 from klanmiko/enhancement/archive
Fix #161 reminder disable on archive
2016-08-22 22:38:03 -04:00
klanmiko
e5162c48ab Scheduler should ignore archived habits
Also consider changing "archive" to "inactive"
2016-08-22 19:00:36 -07:00
b54c4de5f7 ShowHabitRootView: update title automatically
Fixes #162
2016-08-22 17:02:20 -04:00
2bdc0b4f5e Update unit tests for ListHabitsMenu 2016-08-22 16:48:58 -04:00
22db61db01 Fix crash on startup and after dismissing notification 2016-08-22 16:40:23 -04:00
c7cfbd1643 Allow cards to be rendered by the layout editor 2016-08-07 08:43:18 -04:00
8655437f3e Change 'show completed' to 'hide completed' 2016-08-05 19:37:47 -04:00
3ad3cf54ec Add tests for HabitCardView 2016-08-05 07:54:06 -04:00
9a470cc61d Initialize db earlier; fixes crash 2016-08-05 06:30:32 -04:00
fc2087fe68 Write tests for ListHabits controller, menu and screen 2016-08-04 21:56:38 -04:00
7b8ab6a625 Dismiss notification automatically 2016-08-04 07:46:10 -04:00
2d40fb0b82 Write tests for receivers 2016-08-04 07:34:38 -04:00
c961045b63 Call refresh from UI thread 2016-08-04 06:15:43 -04:00
d0db3359fe Allow StyledResources to receive a fixed theme for testing 2016-08-03 22:27:49 -04:00
11378e07bf Properly initialize appComponent 2016-08-03 22:13:30 -04:00
8132188e46 Replace Singleton by AppScope 2016-08-03 07:48:58 -04:00
7eb454788f Refactor receivers 2016-08-03 07:37:00 -04:00
c262adbe85 Move Preferences to their own package 2016-07-31 08:47:20 -04:00
05aa5b1172 Replace InterfaceUtils theme methods by ThemeSwitcher 2016-07-31 08:42:40 -04:00
dd3d78b82c Filter Dagger classes from coverage report 2016-07-31 08:39:47 -04:00
3d4ae2126b Remove DialogFactory 2016-07-31 08:03:07 -04:00
5aa9114aff Fix name of Ukrainian language 2016-07-30 21:58:06 -04:00
77e0ad007f Update list of developers 2016-07-30 21:56:27 -04:00
2e53b75705 Merge pull request #151 from sciamano/freq2
relative size of the marks in the frequency view
2016-07-30 21:24:30 -04:00
Denis
0ec03035f5 maximal frequency of the data 2016-07-30 21:26:09 +02:00
Denis
67ae48b527 radius and color of the mark in the frequency view 2016-07-30 21:26:06 +02:00
Denis
82d2931559 fix code duplication 2016-07-30 21:26:01 +02:00
a527140802 Use the same HabitCardListCache for all activities
Fixes #147
2016-07-29 07:40:56 -04:00
9ec3d9048a Make Preferences listen for external changes 2016-07-29 07:28:44 -04:00
3ed4f3b280 Make scrolling very smooth again 2016-07-29 07:21:47 -04:00
f101975320 Take timezone into account when scheduling alarms 2016-07-29 07:07:02 -04:00
3a7f27755c Use hashmap on AndroidTaskRunner 2016-07-29 07:06:49 -04:00
3a3be664f7 Fix some bugs on fragments and model listeners 2016-07-28 21:45:54 -04:00
33ae8d1edf Show empty message at startup 2016-07-28 21:45:54 -04:00
cb3b8f2cde Reorganize sections and add GPLv3 logo 2016-07-28 18:30:11 -04:00
307e025b1a Fix CheckmarkListTest 2016-07-28 08:00:01 -04:00
03dd24d17b Clean up BaseUnitTest 2016-07-28 07:42:03 -04:00
5f4ac21a41 Small changes 2016-07-27 20:29:13 -04:00
b33616d363 Remove old .gitmodules 2016-07-27 19:30:37 -04:00
b7d1c6d254 Update copyright notice 2016-07-27 19:01:05 -04:00
bd30ceee1b Fix small layout issue. 2016-07-27 18:58:51 -04:00
fe96313162 Update NOTICE.md 2016-07-27 18:56:24 -04:00
938fe3325e Remove disabled test 2016-07-27 18:13:02 -04:00
237de035bb Remove remaining static references to app component 2016-07-27 18:09:36 -04:00
ddc85ced0b Remove waitForTasks 2016-07-27 17:10:07 -04:00
eceb1bfb7d Remove most static references to app component; fix tests 2016-07-27 17:05:51 -04:00
3b737996e9 Reorganize packages 2016-07-27 00:03:59 -04:00
7f75f9b6da Construct ListHabits using dagger 2016-07-26 23:52:39 -04:00
74f78f0fdf Improve usage of dagger 2016-07-26 19:26:42 -04:00
ef63dd19e7 Fix reference to MainActivity 2016-07-25 17:03:09 -04:00
c7878d979f Switch from toasts to snackbars 2016-07-25 08:58:28 -04:00
ebd294be63 Split InterfaceUtils 2016-07-25 08:31:26 -04:00
748cec06a8 Dagger: replace injects by getters 2016-07-23 21:18:20 -04:00
15a4a2c002 Refactor reminders; replace int by WeekdayList 2016-07-23 19:19:22 -04:00
37a9e793e7 Add tests for HabitCardListCache; refactor TaskRunners 2016-07-23 18:06:03 -04:00
d54de9df89 Refactor tasks; break progress bars 2016-07-23 11:45:59 -04:00
94a5db2208 Add view tests 2016-07-22 08:01:44 -04:00
a984467516 Some refactoring; add tests for ListHabitsScreen 2016-07-21 22:48:37 -04:00
d6dacfd24b Refactor reminder scheduling, add tests 2016-07-20 17:01:24 -04:00
3938ae6fa8 Fix test sizes 2016-07-18 20:57:21 -04:00
7dbbc51a9a Add task for combined coverage report 2016-07-18 10:53:23 -04:00
c9d1bb821c Refactor pending intents 2016-07-18 09:07:21 -04:00
77f406dcee Refactor broadcast receivers and add a public receiver
Fixes #6
2016-07-17 17:28:14 -04:00
61b0b1fdea Use separate broadcast actions for widgets and notifications 2016-07-17 09:32:42 -04:00
8cde0d6aca HistoryChart: make toggling faster 2016-07-17 09:32:21 -04:00
fa9f90a09e Refactor HeaderView; update list on resume 2016-07-17 08:59:34 -04:00
28eb615b0e Notification: add checkmark instead of toggling 2016-07-17 08:43:14 -04:00
c1e10e09a5 Refresh list after importing DB 2016-07-17 08:30:07 -04:00
35e93fddc6 Use dynamic number of streaks on widget 2016-07-17 08:28:19 -04:00
33596a2797 Restore dynamic number of checkmarks 2016-07-17 08:02:31 -04:00
207f026ceb Fix habit creation 2016-07-16 10:35:55 -04:00
0f12d02990 Use same cache for every SQLHabitList 2016-07-16 10:12:06 -04:00
bc4bbaefac Remove some drag-and-drop glitches 2016-07-15 09:33:57 -04:00
a802053ef7 Update .gitignore 2016-07-15 09:33:07 -04:00
cf98d1a5c3 AndroidTest: Increase timeout 2016-07-14 11:54:15 -04:00
cc1e56894b Fix test scripts 2016-07-14 11:30:21 -04:00
06b5f89b7a Make habit deletions instantaneous
Fixes #133
2016-07-14 10:57:45 -04:00
17423b3ecd Fix most issues with the RecyclerView; improve loading 2016-07-14 10:15:56 -04:00
1526f617c5 Remove most animation glitches on RecyclerView; disable reordering 2016-07-11 22:06:30 -04:00
365eb400d0 Add proguard file 2016-07-11 09:04:36 -04:00
785d57c778 Merge branch 'dev' of github.com:iSoron/uhabits into dev 2016-07-11 09:03:24 -04:00
8e9f1aa166 Add basic API for the Pebble app 2016-07-11 08:47:00 -04:00
b2f97cb0a5 Merge pull request #145 from ajbarnes/dev
Add 24hr snooze. Fixes #103
2016-07-06 04:39:00 -04:00
Adam Barnes
4c1ff4f0a8 Add 24hr snooze. Fixes #103 2016-07-05 23:44:16 -04:00
94a48133ec Merge branch 'feature/recyclerview' into dev 2016-07-01 10:22:06 -04:00
71dd6cde89 Restore drag & drop and click; remove DragSortListView library 2016-07-01 09:23:07 -04:00
b33420cabb Replace ListView with RecyclerView 2016-07-01 08:03:30 -04:00
dbe268b8e9 Add simple filter to menu 2016-06-30 07:23:28 -04:00
3d53812d7f Restore CircleCI badge 2016-06-29 21:31:34 -04:00
5b4b436f0f CircleCI: use JDK8 2016-06-29 21:10:22 -04:00
6ef2983906 Implement filter by colors 2016-06-29 20:12:09 -04:00
b749f7f391 Fix CircleCI scripts 2016-06-29 20:11:39 -04:00
922b234307 Initial implementation of filters 2016-06-29 19:49:19 -04:00
31fdae1c8b Merge branch 'feature/refactoring-mvc' into dev 2016-06-29 12:23:04 -04:00
5d61fdd3d0 Write tests for HintView 2016-06-29 10:03:56 -04:00
588b45d47b Merge branch 'dev' into feature/refactoring-mvc 2016-06-28 22:48:45 -04:00
9a059275ce Merge branch 'dev' into feature/refactoring-mvc 2016-06-28 22:46:17 -04:00
4ae813e6f9 Restore all widgets 2016-06-28 22:39:24 -04:00
52701666bc Start refactoring widgets 2016-06-28 12:11:34 -04:00
a90e26691f Fix HistoryEditorDialog 2016-06-26 17:10:17 -04:00
f0c62a5908 Update run_tests script to run JVM tests 2016-06-26 17:07:38 -04:00
e899a70eb0 Fix most tests 2016-06-25 05:53:19 -04:00
aa41717c66 Remove CircleCI temporarily 2016-06-25 05:43:49 -04:00
6ba6d7c8c1 Refactor ShowHabit screen; remove fragment 2016-06-24 14:52:41 -04:00
efd0d1e051 Refactor ShowHabit fragment; break widgets 2016-06-24 08:39:48 -04:00
a11ad6e909 ShowHabit: Refactor score chart 2016-06-22 19:47:16 -04:00
a060cbe578 ShowHabit: Refactor subtitle and overview 2016-06-22 08:48:08 -04:00
a445ca962b Merge tag 'v1.5.6' into dev
v1.5.6
2016-06-19 17:10:17 -04:00
2b6fc06b86 Merge branch 'hotfix/1.5.6' 2016-06-19 17:10:12 -04:00
98ad3aab9d Make checkmark widgets work again 2016-06-19 17:06:03 -04:00
c7e63a40da Bump version to 1.5.6 2016-06-19 17:04:25 -04:00
b6ed33b1e6 Merge branch 'dev' into feature/refactoring-mvc 2016-06-19 16:16:04 -04:00
91e39372a1 Merge tag 'v1.5.5' into dev
v1.5.5
2016-06-19 10:08:26 -04:00
c2dd26eeb3 Merge branch 'hotfix/1.5.5' 2016-06-19 10:08:19 -04:00
5831340343 Update French translation; add Slovenian and Croatian translations 2016-06-19 09:57:45 -04:00
d7f6f52a49 Always refresh data after resuming
Fixes #120
2016-06-19 09:32:42 -04:00
93b442332d Bugfix: add check on correct date when checking from notification 2016-06-19 09:08:34 -04:00
e248824bcd Bump version to 1.5.5 2016-06-19 08:08:00 -04:00
ec4a381d70 Refactor Habit class 2016-06-17 07:29:48 -04:00
b13f2b4228 Create a class for Reminders 2016-06-16 15:19:02 -04:00
efc7b2cebb Replace ActiveAndroid queries with raw SQLite queries 2016-06-16 13:51:36 -04:00
add08d6054 Fix reordering 2016-06-15 10:12:33 -04:00
5d8a348aaf Hide empty message at startup 2016-06-15 09:54:39 -04:00
ec0e8ac24c Cancel selection with back button correctly 2016-06-15 09:48:01 -04:00
fee3137a6f Move ripple to correct place 2016-06-15 05:39:13 -04:00
abe6b10964 Restore dynamic checkmark button count 2016-06-15 05:32:20 -04:00
6484b96e5a Reformat and reorganize some code 2016-06-15 05:04:12 -04:00
3d3d5b9b96 Fix top navigation on Settings and About screens 2016-06-15 05:02:13 -04:00
14364901ff Reorganize instrumented tests 2016-06-14 05:46:31 -04:00
440706882b Move command tests to JVM 2016-06-14 05:30:30 -04:00
ca9d56e59e Fix toolbars 2016-06-12 18:22:04 -04:00
9a6dafaa79 Improve documentation 2016-06-12 09:23:55 -04:00
9a44774284 Add instrumented unit tests for SQLite lists 2016-06-12 07:55:47 -04:00
2b23b36e36 Move remaining model tests to JVM; simplify SQLite implementation 2016-06-10 18:55:32 -04:00
78d4f86cab Separate ActiveAndroid from models 2016-06-10 13:30:33 -04:00
18e8390aed Merge branch 'dev' into feature/refactoring-mvc 2016-06-05 04:37:46 -04:00
cabcd5b1bf CSV export: allow spaces on filename and fix tests 2016-06-04 22:06:01 -04:00
7e8a2a0c1c Major refactoring of ListHabitsActivity 2016-06-04 18:10:33 -04:00
650971bf36 Merge tag 'v1.5.4' into dev
Version 1.5.4
2016-05-29 12:06:46 -04:00
00774368d4 Merge branch 'hotfix/1.5.4' 2016-05-29 12:06:36 -04:00
cbf1bd3e19 Update changelog 2016-05-29 11:57:33 -04:00
4061921b93 Add Serbian translation 2016-05-29 11:56:57 -04:00
59d42fe62f Show empty ringtone name in case of RuntimeException
Fixes #116
2016-05-29 11:47:36 -04:00
3ffa079e24 Remove code duplication 2016-05-28 13:03:37 -04:00
bb950d61fc Move sendFile and sendEmail to Screen 2016-05-28 12:21:38 -04:00
0ad0e5cf36 Fix time out on tests 2016-05-28 12:20:02 -04:00
3b56d6d596 Add missing file 2016-05-28 10:16:21 -04:00
8d57273987 Refactor EditHabitDialog 2016-05-28 10:15:55 -04:00
b98853ab26 Introduce ButterKnife 2016-05-27 23:02:04 -04:00
071cad73d4 Make models observable, refactor ShowHabitFragment 2016-05-27 22:26:33 -04:00
16dc9c25d2 Fix bug that caused checkmarks not to update sometimes 2016-05-27 14:06:55 -04:00
6b23858136 Create HabitListView and move most code from Fragment to it 2016-05-27 12:19:29 -04:00
e0198e9926 Reorganize files 2016-05-27 10:20:16 -04:00
6445bf62bc Refactor MainActivity and Preferences 2016-05-26 16:35:47 -04:00
83ef8564e1 Reorganize file tree according to feature 2016-05-26 10:38:59 -04:00
88e8aad0d8 CSV export: sanitize habit name before creating folder
Fix #113
2016-05-25 10:09:39 -04:00
a4b6728721 Bump version to 1.5.4 2016-05-25 10:09:11 -04:00
ca9745f550 Merge tag 'v1.5.3' into dev
Version 1.5.3
2016-05-22 12:56:36 -04:00
33bc8f78e7 Merge branch 'hotfix/1.5.3' 2016-05-22 12:56:28 -04:00
0f223f8504 Update changelog 2016-05-22 12:56:15 -04:00
007996c69e Update translations 2016-05-22 12:50:42 -04:00
ff9d50b32a Remove debug code 2016-05-22 12:29:37 -04:00
38024d71ce Allow checkmark widget to be resized to very small sizes 2016-05-22 12:24:28 -04:00
9ec1afc208 Remove associated preference after deleting widget 2016-05-22 10:28:58 -04:00
e0888a9b4d Remove fields from BaseWidgetProvider
This caused a bug when multiple widgets had different sizes. The size of the
last widget would be used in all of them.
2016-05-22 10:27:27 -04:00
8aade2f145 Fix crash when habits with reminders have short names
Fixes #109
2016-05-22 09:33:30 -04:00
1ba1d775f7 Bump version to 1.5.3 2016-05-22 09:24:20 -04:00
605593d739 Update UI tests 2016-05-19 12:51:36 -04:00
bfbfcbd8cf Merge tag 'v1.5.2' into dev
Version 1.5.2
2016-05-19 12:01:37 -04:00
bd6cba8303 Merge branch 'hotfix/1.5.2' 2016-05-19 12:01:29 -04:00
befc3a0ad8 Update changelog 2016-05-19 12:01:24 -04:00
f4c963e2c1 Update Japanese translation 2016-05-19 11:58:20 -04:00
e9a4a047c1 Trim habit names on log files 2016-05-19 11:50:38 -04:00
7ce1988d2e Fix NullPointerException on RingView 2016-05-19 11:46:02 -04:00
53911fa410 Send bug report on the body of email, instead of attachment 2016-05-19 11:39:17 -04:00
f5f43d9a16 Bump version to 1.5.2 2016-05-19 10:40:45 -04:00
dbf569ff87 Merge tag 'v1.5.1' into dev
v1.5.1
2016-05-17 09:36:00 -04:00
d0e475ad78 Merge branch 'hotfix/1.5.1' 2016-05-17 09:35:51 -04:00
b3199cf092 Update version and changelog 2016-05-17 09:35:10 -04:00
73e6f2a2d4 Remove ActiveAndroid.jar 2016-05-17 09:29:53 -04:00
2c80544aaa Merge tag 'v1.5.0' into dev
Version 1.5.0
2016-05-15 08:43:39 -04:00
097a5be864 Merge branch 'release/1.5.0' 2016-05-15 08:43:26 -04:00
0287473e61 Update changelog 2016-05-15 08:40:58 -04:00
f403dc6f77 Update view tests 2016-05-15 08:40:27 -04:00
23466523df Remove minTextSize
This was causing problems, since some launchers completely
ignore minimum widget sizes. When widgets were too small,
the text started to overflow.
2016-05-15 08:21:46 -04:00
83ca136346 Update changelog 2016-05-13 07:09:09 -04:00
7830b599db Bump version to 1.5.0 2016-05-13 06:52:32 -04:00
49376d4f41 Minor changes to widget colors 2016-05-13 06:50:35 -04:00
8ee9c9c0b1 Update screenshots 2016-05-13 06:50:22 -04:00
ee9da29422 Update introduction 2016-05-13 06:42:22 -04:00
ae7b0408b2 Update list of complete translations 2016-05-13 06:29:19 -04:00
d926d9dbd6 Keep name of the app consistent across translations 2016-05-13 06:14:40 -04:00
5694da2413 Add new translations and update list of contributors 2016-05-13 06:08:09 -04:00
7e0ae144b8 Update translations 2016-05-13 04:56:02 -04:00
1426d887e7 CircleCI: Ignore some branches 2016-05-13 04:41:15 -04:00
acb8e820fd Use AlertDialog from Support Library 2016-05-11 07:38:43 -04:00
eae0d66f51 Dismiss notification automatically when user adds a checkmark 2016-05-11 07:30:45 -04:00
e933cbbc43 Fix up navigation when opening the app from widget
Fixes #94
2016-05-11 06:44:11 -04:00
0b95b6a78c Install build-tools before compiling 2016-05-08 08:51:08 -04:00
5e873a3659 Make frequency text more natural 2016-05-08 08:46:45 -04:00
d292ecd988 Increase default amount of memory for gradle 2016-05-08 08:24:14 -04:00
19b484c368 Fix crash on settings screen 2016-05-08 08:23:50 -04:00
834ae92d87 Reschedule alarms on boot
Fixes #93
2016-05-08 08:04:24 -04:00
26ce92d381 Update versions 2016-05-08 07:38:57 -04:00
0eb12812d4 Allow user to reverse the order of days 2016-05-04 06:30:06 -04:00
a1a636c718 Make ScoreView use custom interval also on widgets 2016-05-01 10:06:02 -04:00
b2811b9797 Allow user to select reminder sound
Closes #63
2016-05-01 09:43:28 -04:00
4db7a6e89c Show larger ripple when toggling check marks
Closes #78
2016-05-01 08:21:36 -04:00
677a643e5b Allow theme to be fixed during tests 2016-04-30 19:13:36 -04:00
dbca3238f6 Small changes to ScoreView 2016-04-30 17:14:44 -04:00
ae5dd700f3 Fix widget colors and size 2016-04-30 16:42:27 -04:00
7e4c508d7d Merge branch 'feature/ring-view' into dev 2016-04-30 12:44:25 -04:00
d0c056eeaa Update screenshots 2016-04-30 12:43:18 -04:00
f172c69eed Update tests 2016-04-30 12:30:20 -04:00
0cd4a41438 Bump gradle version 2016-04-29 11:48:46 -04:00
d77c78249c Tweak layout to work with older phones with smaller screen 2016-04-29 11:48:06 -04:00
d9d48ff984 Minor UI changes 2016-04-29 07:55:45 -04:00
7be71ba4f8 Fix icon colors and remove some warnings 2016-04-29 07:44:06 -04:00
f0534fefbc Show more information on score overview 2016-04-29 07:35:38 -04:00
30ef75bb45 Alternative design for header bar 2016-04-29 06:40:02 -04:00
44ed74a693 Correct colors 2016-04-29 06:38:19 -04:00
4aea527e07 Fix some crashes on widgets 2016-04-27 06:01:10 -04:00
638539259a Bump gradle version 2016-04-26 20:09:06 -04:00
75dec88411 Update widget previews 2016-04-26 20:08:59 -04:00
2dc4fcbc46 Fix transparency on widgets 2016-04-25 22:22:37 -04:00
7977d5247c Tweak transparency and colors 2016-04-25 21:03:23 -04:00
8b18e32a16 Create color palette for widgets 2016-04-24 06:42:14 -04:00
dbcad9a5f4 Use dark theme for widget colors 2016-04-24 06:42:14 -04:00
3bba75ff50 Add ripple effect to check mark widget 2016-04-24 06:42:14 -04:00
9bbeee66e2 Impose max number of lines on habit list 2016-04-24 06:42:14 -04:00
c6e76f4cfd Show multiple lines on check mark widget 2016-04-24 06:42:14 -04:00
980db1d171 Add ring to check mark widget 2016-04-24 06:42:14 -04:00
aee5d975db Replace star by ring 2016-04-24 06:42:14 -04:00
e2bb4371d3 Fix UI test for settings screen 2016-04-24 06:37:15 -04:00
fcee8552f0 Switch to compat ActionMode; fix tests 2016-04-24 05:55:42 -04:00
a4864e4612 Extract some strings; move definitions to correct file
Fixes #81
2016-04-21 06:05:38 -04:00
8ddf4574e3 Fix failed view tests 2016-04-20 09:27:44 -04:00
f1ca8c5449 Merge branch 'feature/material-compat' into dev 2016-04-20 07:52:06 -04:00
81a188f125 Customize action mode colors 2016-04-20 07:50:12 -04:00
44402bf3a4 Fix progress bar position 2016-04-20 07:31:02 -04:00
7f159149ef Minor layout changes on the spinners 2016-04-19 22:50:41 -04:00
0c1e8d5131 Switch to AppCompatDialogFragment and compat Fragment 2016-04-19 22:10:25 -04:00
a4e3f7e037 Fix spinners on EditHabitDialog 2016-04-19 21:53:14 -04:00
b354a0765b Use grey toolbar on pre-Lollipop 2016-04-19 21:02:05 -04:00
bbd959dfda Fix borders on HistoryEditorDialog 2016-04-19 09:01:36 -04:00
d05d404c55 Show toolbar shadow on pre-Lollipop 2016-04-19 08:27:45 -04:00
8938b0c9a6 Improve settings screen 2016-04-19 07:50:05 -04:00
bd6fcd066c Fix dialog borders 2016-04-19 05:36:34 -04:00
6a5f2abb76 Switch to AppCompatActivity 2016-04-18 07:59:41 -04:00
33b5215b00 Use SwitchPreference instead of checkmarks 2016-04-18 06:21:04 -04:00
5d808dd2e8 Update padding on widgets 2016-04-16 09:20:33 -04:00
767ec1b6de Minor layout improvements for HabitScoreView 2016-04-16 09:16:39 -04:00
9e5f3d8f58 Fix blinking when updating widgets 2016-04-16 09:03:45 -04:00
775d028629 Add missing icon 2016-04-16 08:51:56 -04:00
4bfb839370 Draw separate widgets for landscape and portrait modes
Fixes #76
2016-04-16 08:51:20 -04:00
8f64b696d8 Remove debug messages 2016-04-16 08:10:12 -04:00
c4bf31e778 Adjust text size and position on HistoryView and StreakView 2016-04-16 08:06:01 -04:00
c757ce6548 Enforce min text size on HabitScoreView 2016-04-16 07:36:39 -04:00
78994bb3c4 Add screenshots for night mode 2016-04-15 19:12:45 -04:00
2373ba5407 Update screenshots 2016-04-15 19:10:38 -04:00
b601a643dd Merge branch 'feature/dark-theme' into dev 2016-04-15 18:29:39 -04:00
90e513e778 Remove warning when palette cannot be found 2016-04-15 18:26:01 -04:00
728c9557f0 Migrate DB to new color format 2016-04-15 18:25:30 -04:00
5bd0c842f5 Minor typo 2016-04-15 18:10:34 -04:00
97711087f9 Fix invisible text on widgets 2016-04-15 17:49:10 -04:00
2fe2d8834a Fix spinner theme on pre-lollipop 2016-04-15 17:41:16 -04:00
93a893d660 Replace icons with Material icons
Fixes #9
2016-04-15 16:34:22 -04:00
feff9a18e6 Fix action icons 2016-04-15 16:13:52 -04:00
b42565b770 Backport dark theme to pre-lollipop devices 2016-04-13 11:54:24 -04:00
3de702ced2 Fix colors in About screen 2016-04-13 10:44:11 -04:00
42f7f4042d Add option for AMOLED night mode 2016-04-13 10:38:20 -04:00
2115a590f2 Add fade transition when switching themes 2016-04-13 09:53:37 -04:00
f5f3ec9f88 Update tests for custom views 2016-04-12 23:42:02 -04:00
6d5a8f5753 Merge branch 'sciamano-day-of-week' into dev 2016-04-12 23:29:10 -04:00
06fc04092b Code cleanup 2016-04-12 23:21:00 -04:00
Denis
24c02605d1 Select first day of week according to the current locale 2016-04-12 23:11:32 -04:00
11fcb67624 Improve custom views' colors 2016-04-11 18:11:18 -04:00
abc92e390a Update list of translators 2016-04-10 12:59:42 -04:00
52c07660b1 Implement menu item to switch between themes 2016-04-10 10:41:52 -04:00
35f778c376 Do not use habit color as primary color on dark themes 2016-04-10 08:32:01 -04:00
436ae7e574 Update selected_box colors for dark theme 2016-04-10 08:17:36 -04:00
5c683a77c2 Fix ripple on ListHabitsFragment 2016-04-10 08:13:05 -04:00
dd1f6c9efc Change habit.color palette according to current theme 2016-04-09 20:01:35 -04:00
cba089407b Merge tag 'v1.4.1' into dev
Loop 1.4.1
2016-04-09 05:36:12 -04:00
a7b0395a2a Merge branch 'hotfix/1.4.1' 2016-04-09 05:36:05 -04:00
7d2946360f Update changelog 2016-04-09 05:31:07 -04:00
04e8432522 Widgets: show error message instead of crashing 2016-04-09 05:27:39 -04:00
13d34945b4 Merge branch 'poeditor' into hotfix/1.4.1 2016-04-09 05:10:41 -04:00
756578508e Bump version to 1.4.1 2016-04-09 05:10:20 -04:00
38fc7650b9 Update translations 2016-04-09 05:08:02 -04:00
cf06ec0a4b Fix chart colors 2016-04-09 04:58:26 -04:00
f0701f7b35 First version of dark theme 2016-04-08 17:15:14 -04:00
de8018af7d Delete unused resources 2016-04-08 15:53:47 -04:00
ac885e1503 Refactor ListHabitsFragment layouts and styles 2016-04-08 15:50:51 -04:00
f85e09288c Add link to open beta on Google Play 2016-04-07 18:03:46 -04:00
6f11596bb7 Merge tag 'v1.4.0' into dev
Loop 1.4.0
2016-04-07 17:48:50 -04:00
64c4367706 Merge branch 'release/1.4.0' 2016-04-07 17:48:33 -04:00
c08702341a Update Spanish translations 2016-04-07 17:23:01 -04:00
abb8c09bc9 Update widgets after executing command 2016-04-07 17:15:33 -04:00
87255ceb25 RingView: invalidate after updating percentage and color
Fixes #79
2016-04-07 17:02:02 -04:00
aedbfded0f Show POEditor link if language is not fully translated 2016-04-06 06:33:52 -04:00
779c0040bd Add some options back to settings screen 2016-04-05 13:32:46 -04:00
7bba6a887f Increase width of chart labels as needed 2016-04-05 08:11:37 -04:00
52a1d19793 Allow labels to expand 2016-04-05 06:50:42 -04:00
35e346e7ea Remove empty strings from translations 2016-04-05 06:42:32 -04:00
3102158bdb Merge branch 'poeditor' into release/1.4.0 2016-04-05 06:34:00 -04:00
7afa65a53c Add Czech translation 2016-04-05 06:33:48 -04:00
2e5533d480 Update translations (POEditor) 2016-04-05 06:28:35 -04:00
b29c7e695a Save failed views to SD card root if writable 2016-04-05 05:47:09 -04:00
7df745eb82 Update CHANGELOG 2016-04-04 20:49:57 -04:00
c0d518ca4d Bump version to 1.4.0 2016-04-04 19:48:49 -04:00
eeff14a2ca Bump version to 1.4.0 2016-04-04 19:31:35 -04:00
af3b18f518 Fix incorrect date format for some languages 2016-04-04 19:05:24 -04:00
3811f4b339 Rename tests for compatibility with AWS Device Farm 2016-04-04 11:15:17 -04:00
50a8aece6f Move expensive call to background thread 2016-04-04 08:18:59 -04:00
2f66cfc417 Minor string change 2016-04-04 08:07:10 -04:00
132ce36a57 More detailed logs in case getFilesDir fails 2016-04-04 07:58:07 -04:00
147f010d1b Update website URL 2016-04-04 07:57:28 -04:00
afc91ed89a Merge branch 'feature/performance' into dev 2016-04-04 07:04:22 -04:00
684c37d167 Update list of translators 2016-04-04 07:02:41 -04:00
8b296ce2ae Make setup method public 2016-04-04 06:56:02 -04:00
e2e9e2e27a Remove debug code 2016-04-04 06:54:12 -04:00
8b2f31c44a Update widget preview 2016-04-04 06:53:55 -04:00
83db9fe2f2 Hide progress bar on startup 2016-04-04 06:46:16 -04:00
9891139b5a Fix tests on ICS 2016-04-04 06:43:42 -04:00
cbc8cbfea3 Fix scores with null habit 2016-04-04 06:02:56 -04:00
96c46b655d Minor refactoring 2016-04-04 05:43:07 -04:00
0de52d4fa3 BaseTask: move register/unregister to onPre/PostExecute 2016-04-04 05:42:27 -04:00
16d85dc4c7 Update README.md 2016-04-03 12:41:57 -04:00
c51b1fd399 Remove progress bar; switch to BaseTask 2016-04-03 07:51:54 -04:00
eb8dd1d450 Simplify and optimize loading time of ShowHabitFragment 2016-04-03 07:46:08 -04:00
357646152f Use postInvalidate instead of invalidate 2016-04-03 07:27:37 -04:00
ae5be202b2 Add convenience methods for tracing 2016-04-03 07:27:16 -04:00
cc3e2153d0 Allow BaseTask to publish integer progress 2016-04-03 07:26:40 -04:00
7433a2413d Refactor BaseTask interface 2016-04-03 06:45:10 -04:00
1cd8eb6849 NumberView: do not request layout on setNumber 2016-04-03 05:11:45 -04:00
7f009e2365 Create indexes in database 2016-04-03 05:10:13 -04:00
4cec7d7367 Implement methods to generate huge test database 2016-04-03 05:08:57 -04:00
f59c0bd7ff Replace badges 2016-04-02 13:58:38 -04:00
2d2bb8b4ed Render widgets in background 2016-04-02 13:32:04 -04:00
e476096ae1 Fix view tests 2016-04-02 11:47:28 -04:00
e2c814a982 Fix ShowHabitFragment and HistoryEditorDialog 2016-04-02 10:48:33 -04:00
4fcaa68baf Throw exception when slow methods are executed on the main thread 2016-04-02 10:27:45 -04:00
02e45dbe27 Rename DialogHelper to UIHelper 2016-04-02 10:14:21 -04:00
bf6562f854 Update views on background thread 2016-04-01 17:50:14 -04:00
3c927e009a Improve performance of Score computation 2016-04-01 17:49:17 -04:00
a9bcae0f2f Clean up CheckmarkView 2016-04-01 17:24:12 -04:00
bc54f3f916 Improve performance of toggleCheckmark 2016-04-01 16:21:22 -04:00
5763d76167 Make setup method public 2016-04-01 16:08:51 -04:00
86b66b1388 Add tests for DeleteHabitsCommand 2016-03-31 08:02:44 -04:00
5e0422848b Add tests for EditHabitCommand 2016-03-31 07:54:00 -04:00
a67a3297b1 Add tests for ToggleRepetitionCommand 2016-03-31 07:34:20 -04:00
b2f2d4ad28 Add tests for UnarchiveHabitsCommand 2016-03-31 07:24:43 -04:00
4dc8201dcb Add tests for CreateHabitCommand 2016-03-31 07:20:20 -04:00
f24e300b40 Implement tests for Archive and ChangeColor commands 2016-03-31 07:11:24 -04:00
21157eaa19 Increase similarity threshold 2016-03-31 07:09:47 -04:00
0ea2c8e5c8 HistoryView: test tap at invalid locations 2016-03-31 06:31:13 -04:00
826491bc90 Test custom views with transparent background 2016-03-31 06:21:01 -04:00
72f5ca5531 Add tests for NumberView 2016-03-31 06:03:16 -04:00
f9da90a93a Add tests for HabitStreakView 2016-03-30 21:02:05 -04:00
96c6a97ad0 Add tests for HabitScoreView 2016-03-30 19:46:37 -04:00
20469789ea Add tests for HabitFrequencyView 2016-03-30 18:41:00 -04:00
abafffcff1 Increase similarity threshold 2016-03-30 12:25:45 -04:00
d45a4445cc Minor changes to HabitHistoryView rendering; enable haptic feedback 2016-03-30 12:25:24 -04:00
94f56a8869 Make all unit tests inherit from BaseTest 2016-03-30 08:49:42 -04:00
bc331d0a4d Write tests for HabitHistoryView 2016-03-30 08:28:21 -04:00
2ba7246e5f Refactor Tasks for better testability 2016-03-30 08:28:06 -04:00
67b88c5012 Show log when build fails 2016-03-30 08:24:42 -04:00
e0b637e84d Merge branch 'feature/unit-test-views' into dev 2016-03-28 21:14:42 -04:00
e14d73cf32 Add translator 2016-03-28 21:00:07 -04:00
16b9584868 Minor changes to strings.xml 2016-03-28 20:20:18 -04:00
c091116105 Compute padding/margins more precisely on CheckmarkView 2016-03-28 20:06:32 -04:00
37e1a6233c Copy failed generated images to CircleCI artifacts folder 2016-03-27 18:47:26 -04:00
6838a0f2d3 Restore missing asset 2016-03-27 06:40:56 -04:00
53e737c169 Allow API version-specific view tests; use relative image distance 2016-03-27 06:39:40 -04:00
285f79ed95 Add tests for CheckmarkView 2016-03-26 20:11:17 -04:00
4f65c8bef1 Add script to pull failed generated images from device 2016-03-26 19:47:53 -04:00
51b7cfc8fa Remove useless asset 2016-03-26 19:47:04 -04:00
55ed2585ea Additional tests for RingView 2016-03-26 19:46:33 -04:00
2c62e1578a Create base test class for custom views 2016-03-26 19:25:50 -04:00
e3d96c0431 Upload code coverage to codecov.io 2016-03-26 17:03:21 -04:00
b4c2d2237a Draw month name on the following column.
This reverts commit f9377e17. See #55 for reason.
2016-03-26 08:57:18 -04:00
d14fbbd130 Make streak colors consistent 2016-03-26 08:49:02 -04:00
e2c99d745e Improve visualization of streaks
Closes #20
2016-03-26 08:27:23 -04:00
dfe41176bc Reintroduce vibrate permission 2016-03-25 17:01:41 -04:00
1ef4f8d456 Use system haptic feedback instead of custom vibration; enable on short press
Closes #66
2016-03-25 14:39:57 -04:00
6c810ee7a3 Allow user to send bug report from settings screen 2016-03-25 14:39:57 -04:00
5115379fdd Merge branch 'feature/import-data' into dev 2016-03-25 09:21:47 -04:00
d176ea91fb Use custom matcher for settings activity 2016-03-25 09:04:07 -04:00
62df660079 Implement UI tests for import/export and help 2016-03-25 08:10:10 -04:00
790beb185a Do not save backup when importing Loop DB 2016-03-25 08:09:33 -04:00
59c0af17d7 Use application context to initialize ActiveAndroid 2016-03-25 07:09:41 -04:00
28dad560a6 Write tests for CSV exporter 2016-03-25 07:09:06 -04:00
9e410f8395 Refactor and write tests for IO tasks 2016-03-25 06:18:07 -04:00
3656c51e95 Disable pre-dexing 2016-03-24 22:14:54 -04:00
d3ebb4ff25 Create SD card 2016-03-24 21:52:59 -04:00
3c595bc79d Add null check on DatabaseHelper 2016-03-24 21:51:02 -04:00
1120f56dd4 Write tests for CSV export 2016-03-24 21:36:41 -04:00
2dfbcfcdb0 Revert to alpha version of gradle
Build fails otherwise
2016-03-24 20:14:26 -04:00
90a3964f0f Close database 2016-03-24 18:39:49 -04:00
6c05366f0f Remove illegal characters from filename 2016-03-24 18:39:32 -04:00
dcc771abe7 Chose external dir better 2016-03-24 18:39:14 -04:00
743431ef67 Refactor DatabaseHelper; write tests for data import 2016-03-24 06:59:41 -04:00
d5fc1a6886 Allow user to import full database
Closes #67
2016-03-23 19:21:06 -04:00
eeb0b109ae Remove export CSV from context menu 2016-03-23 19:20:12 -04:00
ad391fa791 Remove extra permissions; better organize files dir 2016-03-23 18:50:02 -04:00
e8bbae8ef9 Allow user to export a full copy of the database 2016-03-23 18:01:25 -04:00
c9793df7c7 Reorganize files 2016-03-23 17:20:26 -04:00
3c6114fc87 Update README.md 2016-03-23 14:19:30 -04:00
4ea4201b6a Merge pull request #69 from XuToTo/patch-1
Update strings.xml
2016-03-23 14:10:42 -04:00
XuToTo
30b5b90091 Update strings.xml
Make some zh-translation words more friendly :)
2016-03-24 00:53:37 +08:00
e6b7b8b590 Export all habits as CSV from the settings menu
Closes #28
2016-03-22 22:54:02 -04:00
2d675ed9b0 Use DB transaction to perform import 2016-03-22 22:54:02 -04:00
1db2f69f05 Import data from HabitBull
Closes #44
2016-03-22 22:54:02 -04:00
49a80faca3 Trap all exceptions 2016-03-22 22:54:02 -04:00
aa5df56437 Use stable version of gradle 2016-03-22 22:54:01 -04:00
581197be03 Import data from Tickmate and Rewire
Closes #36, closes #41
2016-03-22 22:53:16 -04:00
dfe5c4954e Refactor CSVExporter 2016-03-21 06:25:48 -04:00
a0582b65d5 Merge tag 'v1.3.3' into dev
Version 1.3.3
2016-03-20 17:55:16 -04:00
9edc3e12c9 Merge branch 'hotfix/1.3.3' 2016-03-20 17:55:07 -04:00
7bb3db213f Update changelog 2016-03-20 17:46:10 -04:00
e05a69b527 Merge branch 'poeditor' into hotfix/1.3.3 2016-03-20 17:31:08 -04:00
d5df6ddcdb Add Spanish and Korean translations
Closes #40
2016-03-20 17:30:55 -04:00
3016263750 Update translations 2016-03-20 17:25:06 -04:00
f9377e1768 Draw month name on the correct column
Fixes #55
2016-03-20 17:10:57 -04:00
ae0dad9120 Use DateHelper instead of instantiating GregorianCalendar directly
Fixes #58
2016-03-20 17:06:22 -04:00
be114bde7f Bump version to 1.3.3 2016-03-20 17:01:58 -04:00
e7148abc2e Open statistics page when user taps on widget
Closes #19
2016-03-20 08:36:16 -04:00
2d87076a48 Change the way score are grouped 2016-03-20 08:19:57 -04:00
7da4ddf91b Allow user to change score view interval
Closes #10
2016-03-20 08:06:12 -04:00
e4e8d77acc Add link to FAQ
Closes #24
2016-03-19 13:30:04 -04:00
7945a5faf9 Set default habit frequency to daily 2016-03-19 11:58:25 -04:00
3f7d25461d Wake up device in Doze mode 2016-03-19 10:19:28 -04:00
519737a1c3 Minor changes to English strings 2016-03-19 10:02:49 -04:00
c3ff1fbe03 Add quick selection for commonly used habit frequencies
Closes #25
2016-03-19 09:46:42 -04:00
d39e1978a2 Add Spanish and Korean translations
Closes #40
2016-03-19 05:03:23 -04:00
536c095f23 Merge tag 'v1.3.2' into dev
Version 1.3.2
2016-03-18 11:36:48 -04:00
8aaa5aca28 Merge branch 'hotfix/1.3.2' 2016-03-18 11:35:24 -04:00
5540a66e08 Minor spelling change 2016-03-18 11:34:05 -04:00
01ffc6c144 Update changelog 2016-03-18 11:25:57 -04:00
21aa658acc Fix crash when input is empty 2016-03-18 11:25:34 -04:00
e17667d85d Fix small layout issue on RingView 2016-03-18 11:25:19 -04:00
f2948dac6f Bump version to 1.3.2 (12) 2016-03-18 11:12:33 -04:00
31a7d92f77 Update translations 2016-03-18 11:11:04 -04:00
82d7970f86 Update list of translators 2016-03-18 11:11:04 -04:00
af1a21a959 Add Swedish translation 2016-03-18 11:11:04 -04:00
e7c1575083 Add Russian translation 2016-03-18 11:11:04 -04:00
d5a14ca55b Add Polish translation 2016-03-18 11:11:04 -04:00
c4d6b80944 Add Italian translation 2016-03-18 11:11:04 -04:00
c4b6cad6bb Update German translation 2016-03-18 11:11:03 -04:00
7070530d0e Update French translation 2016-03-18 11:11:03 -04:00
1332a70eb4 Declare RTL support in the manifest 2016-03-18 11:11:03 -04:00
11c9a3dab0 Add Arabic translation 2016-03-18 11:11:03 -04:00
74f7b0fd28 Update translators' names 2016-03-18 11:11:03 -04:00
e693504183 Update translations 2016-03-18 11:01:25 -04:00
7d9a94ae9e Add line to disable large tests 2016-03-18 10:58:18 -04:00
326cb8f73f Minor changes to javadoc and method visibility 2016-03-18 10:42:47 -04:00
6826bd1ddc Update test 2016-03-18 10:36:56 -04:00
b5bc347624 Use default instead of null for reminderDays 2016-03-18 07:43:11 -04:00
55c058ff42 Minor spelling mistakes 2016-03-18 07:24:26 -04:00
e829bb8109 Enable parallel and daemon (gradle) 2016-03-18 07:18:50 -04:00
0921f9346e Refactor and write docs for Score and ScoreList 2016-03-18 07:16:31 -04:00
eb017bf99b Write missing tests and docs for RepetitionList 2016-03-17 06:49:11 -04:00
79b6ef8200 Improve null check for Checkmark and CheckmarkList 2016-03-17 06:39:29 -04:00
7ba62d6784 Minor formatting 2016-03-17 06:39:12 -04:00
f5e4a88415 Implement missing tests for Habit; remove some dead code 2016-03-17 06:20:52 -04:00
075b7812eb Refactor and write documentation for Habit 2016-03-16 07:49:40 -04:00
3d42505fb9 Merge tag 'v1.3.1' into dev 2016-03-15 20:38:51 -04:00
824f98dd96 Merge branch 'hotfix/1.3.1' 2016-03-15 20:31:06 -04:00
fcb82bcb72 Update changelog for v1.3.1 2016-03-15 20:30:01 -04:00
851cae3662 Show error message on widget when habit not found
Fixes #35
2016-03-15 20:25:47 -04:00
7778c5fb21 Check for null on notifications 2016-03-15 20:17:15 -04:00
ef847dac17 Use StaticLayout to draw RingView label
Fixes #29
2016-03-15 19:56:14 -04:00
8102c18c67 Use long for millisecondsInOneDay
Fixes #34
2016-03-15 19:55:50 -04:00
59ed9ec9bd Bump version to 1.3.1 2016-03-15 19:51:50 -04:00
ffdc923268 Make labels more clear and customizable 2016-03-15 06:10:39 -04:00
9232378d04 Refactor RingView; make text size consistent 2016-03-15 05:30:27 -04:00
b20fd44cbc Scroll to view before clicking 2016-03-14 20:48:39 -04:00
ded8800017 Extract common style 2016-03-14 19:36:05 -04:00
606f66b8d9 Merge branch 'feature/repetition-count' into dev 2016-03-14 19:30:58 -04:00
1bb6cd405b Externalize strings 2016-03-14 19:13:33 -04:00
babf7d64f0 Display repetition count for last week, month, etc
Closes #21
2016-03-14 19:07:32 -04:00
dfbcf78dd7 Update list of translators 2016-03-14 15:46:52 -04:00
e01c668e4d Add Swedish translation 2016-03-14 15:46:40 -04:00
2eef696027 Add Russian translation 2016-03-14 15:46:30 -04:00
ddd10cacd1 Add Polish translation 2016-03-14 15:38:42 -04:00
0cdde4901e Add Italian translation 2016-03-14 15:38:29 -04:00
f13e9b7362 Update German translation 2016-03-14 15:38:12 -04:00
558f72d7c5 Update French translation 2016-03-14 15:33:06 -04:00
6e493f55bc Declare RTL support in the manifest 2016-03-14 15:18:58 -04:00
5186ab840a Add Arabic translation 2016-03-14 15:11:41 -04:00
65fd82d888 Fix indentation 2016-03-14 15:04:15 -04:00
866b62987c Wake up device before running UI tests 2016-03-14 14:55:47 -04:00
18abb2038f Check if habit is null on BaseWidgetProvider 2016-03-14 14:54:43 -04:00
f7f4b5eeb0 Simplify code for drawing header 2016-03-14 14:35:57 -04:00
45a7433773 Use StaticLayout to draw RingView label
Fixes #29
2016-03-14 13:34:21 -04:00
40420c3a77 Save logcat to reports 2016-03-14 08:37:02 -04:00
988b39f2e5 Add missing debug messages to SystemHelper 2016-03-14 08:33:46 -04:00
a0803966f9 Merge branch 'feature/unit-tests' into dev 2016-03-14 08:14:56 -04:00
e4af662836 Merge pull request #33 from vanniktech/patch-1 2016-03-14 08:10:10 -04:00
e95adab422 Include CircleCI badge 2016-03-14 07:39:43 -04:00
012fed01eb Fix CircleCI; remove Travis-CI 2016-03-14 07:39:43 -04:00
4cf2b8072b Unlock screen before running UI tests 2016-03-14 07:39:43 -04:00
196a79a88c Add CircleCI config file 2016-03-14 07:39:43 -04:00
4b7e2e79a7 Update tools versions 2016-03-14 07:39:43 -04:00
9156bba267 Disable animations when testing 2016-03-14 07:39:43 -04:00
1a18bb939d Refactor and write unit tests for RepetitionList 2016-03-14 07:39:43 -04:00
144524e53b Refactor and write tests for checkmarks 2016-03-14 07:39:43 -04:00
3d1c53396c Allow date to be fixed at a certain timestamp 2016-03-14 07:39:43 -04:00
1930db3cd1 Wait after toggling checkmarks 2016-03-14 07:39:43 -04:00
a2c2a5531a Use temporary database for tests 2016-03-14 07:39:43 -04:00
eee2605f74 Add first unit tests for habit 2016-03-14 07:39:43 -04:00
Niklas Baudy
9df0c9ae9e Update EditText of Edit Habit Description to capitalize sentences 2016-03-14 10:19:47 +01:00
e0533505c1 Merge pull request #30 from vanniktech/patch-1
Update EditText of Edit Habit to capitalize sentences
2016-03-14 04:41:09 -04:00
Niklas Baudy
433894336c Update EditText of Edit Habit to capitalize sentences
Basically when entering a word the first letter will be capitalized. Something minor that bugs me always a bit.
2016-03-13 23:46:29 +01:00
9061182301 Update translators' names 2016-03-12 19:39:03 -05:00
8c45f52d56 Merge tag 'v1.3.0' into dev
Release v1.3.0
2016-03-12 06:03:02 -05:00
53c9dca3e2 Merge branch 'release/1.3.0' 2016-03-12 06:02:22 -05:00
8b37daa9fb Update CHANGELOG 2016-03-12 06:02:11 -05:00
f9c2e83c79 Update screenshots 2016-03-12 05:37:00 -05:00
71bdc70c1a Bump version to 1.3.0 2016-03-12 05:25:10 -05:00
5e4a40579a Put about under link category on settings screen 2016-03-12 05:23:34 -05:00
d326be1224 Reintroduce longClick hack 2016-03-12 05:17:55 -05:00
fb8a09c95c Add German translation 2016-03-12 05:13:11 -05:00
1635b9905d Split test 2016-03-12 05:03:04 -05:00
2d88fc0b20 Update Japanese translation 2016-03-11 16:27:40 -05:00
1d74359c06 Update widgets in background 2016-03-11 15:07:56 -05:00
f75f77cec7 Merge branch 'feature/ui-test' into dev
Closes #22
2016-03-11 13:21:10 -05:00
8b10138cd6 Fix tests on pre-Lollipop devices 2016-03-11 13:20:43 -05:00
2b40633110 Add more tests for settings and about 2016-03-11 12:39:59 -05:00
1102d05a61 Test unarchiving habits 2016-03-11 12:39:59 -05:00
51e8c2f111 Implement basic user interface tests 2016-03-11 12:39:59 -05:00
547e4e5f63 Use white background on pre-Lollipop, instead of ripple 2016-03-11 12:39:41 -05:00
84d5c2aac6 Remove longClick hack 2016-03-11 12:26:56 -05:00
2b3b423fa3 Show color button even for a single habit 2016-03-11 12:26:34 -05:00
3b28c37c5e Rebuild order after commit 2016-03-11 12:26:13 -05:00
e749e787ad Add Japanese translation 2016-03-10 06:20:42 -05:00
9c5d582f24 Update copyright notices for translations 2016-03-10 06:10:25 -05:00
75a4edb61c Merge branch 'feature/frequency-view' into dev 2016-03-10 05:52:45 -05:00
34c0758308 Remove debug code 2016-03-10 05:49:34 -05:00
85963ae061 Add frequency widget 2016-03-10 05:43:56 -05:00
e3390d5397 Rename header to frequency; update translations 2016-03-10 05:25:42 -05:00
59a2f31a73 Fix timezone issues; rename class to HabitFrequencyView 2016-03-10 05:25:22 -05:00
c01f8450d3 Update README.md 2016-03-09 14:00:48 -05:00
cea5241135 Implement weekday frequency view 2016-03-09 08:23:55 -05:00
7784fc5c75 Add broadcast receiver to ShowHabitActivity 2016-03-08 22:13:47 -05:00
6dd017f33e Refresh also main activity when history editor closes 2016-03-08 21:57:42 -05:00
c8cd9f85f6 Remove hardcoded string 2016-03-08 21:50:42 -05:00
d038bdb741 Update widgets after history editor closes 2016-03-08 21:50:10 -05:00
f55e8d2c85 Merge branch 'feature/history-editor' into dev
Closes #14
2016-03-08 21:33:27 -05:00
f8dc1d9eae Force rebuild of scores 2016-03-08 21:30:09 -05:00
85393b0d40 Handle configuration changes 2016-03-08 21:22:59 -05:00
75599ad20c Fix timezone issues 2016-03-08 20:58:29 -05:00
c6b948cbf5 Save changes on configuration change
Fixes #16
2016-03-08 18:16:34 -05:00
4d42133a4b Add links to F-Droid 2016-03-08 08:38:16 -05:00
5b151805ff Make HistoryView not editable by default 2016-03-08 07:56:40 -05:00
aa86826bdb Refresh data after closing history editor 2016-03-08 07:53:24 -05:00
821373a340 Make history editor functional 2016-03-08 07:35:55 -05:00
8f37e293b1 Implement dummy history editor; add edit history button 2016-03-08 06:58:34 -05:00
0fb8ed0b53 Add French translation 2016-03-07 20:53:37 -05:00
0c696b2eb2 Include Material Design Icons 2016-03-07 16:41:38 -05:00
cd127bc3f8 Add copyright notices 2016-03-07 16:35:34 -05:00
2cfc809490 Update copyright notices in all files 2016-03-07 15:54:56 -05:00
ba31dee16a Merge branch 'feature/refactoring' into dev 2016-03-07 08:39:11 -05:00
146c743fb8 Simplify list adapter 2016-03-07 08:03:30 -05:00
0c00e9ec2d Simplify constructor 2016-03-07 07:51:48 -05:00
49af55a2de Move more methods to helper 2016-03-07 07:31:06 -05:00
0114b48197 Fix formatting; include license section 2016-03-07 05:44:02 -05:00
09f615a5e6 Update translations 2016-03-07 05:39:15 -05:00
9014acc548 Remove settings menu from ShowHabitActivity 2016-03-07 05:39:15 -05:00
4fb386be86 Include building and installing instructions
Closes #11
2016-03-07 05:29:05 -05:00
ea8606be97 More details to the code contribution subsection 2016-03-06 21:58:02 -05:00
e0527dc8ff Implement about screen 2016-03-06 08:34:18 -05:00
aaf2789a21 Include contributing section 2016-03-06 06:00:12 -05:00
f8dc64cc6b Move time and color pickers resources into separate file 2016-03-05 16:13:48 -05:00
ced5b751be Move methods to helper 2016-03-05 08:43:09 -05:00
8a60dda74e Further simplify ListHabitsFragment 2016-03-05 08:33:17 -05:00
c8c4df6ef7 Split ListHabitsFragment into smaller classes 2016-03-05 07:30:04 -05:00
0c0ac9dee5 Minor formatting 2016-03-05 06:48:25 -05:00
fdf6c91929 Use equals instead of operator 2016-03-05 06:48:01 -05:00
08d6e39a17 Throw exception when trying to undo deletion of habit 2016-03-05 06:46:52 -05:00
b9bc7bd1b5 Merge branch 'master' into dev 2016-03-04 13:28:39 -05:00
7b73238448 Add explicit READ_EXTERNAL_STORAGE permission with maxSdkVersion 2016-03-04 13:19:03 -05:00
83ccfb82ac Merge branch 'release/1.2.0' into master 2016-03-04 12:53:24 -05:00
199d35d9c7 Merge branch 'release/1.2.0' into dev 2016-03-04 12:53:04 -05:00
382a2fe600 Update CHANGELOG.md 2016-03-04 12:46:49 -05:00
e02f9c1d60 Bump version to 1.2.0 2016-03-04 12:46:36 -05:00
5e7636d7ff Fix position for new habits 2016-03-04 12:43:55 -05:00
616322cd35 Fix card background (pre-Lollipop) 2016-03-04 12:43:55 -05:00
299c6a0c1d Show action icons on pre-Lollipop 2016-03-04 12:43:55 -05:00
b4911b6cb4 Save last app version on preferences 2016-03-04 11:13:20 -05:00
f41f877107 Fix data export on older devices 2016-03-04 11:13:20 -05:00
58aa7f6687 Add padding to HabitScoreView 2016-03-04 07:03:05 -05:00
d196e01da0 Update widgets and reminders on background; faster startup 2016-03-04 06:52:31 -05:00
1fbd12a947 Fix incorrect streaks 2016-03-04 06:39:30 -05:00
7493291ade Use average of scores in the interval 2016-03-03 07:38:50 -05:00
2a750704d9 Minor string change 2016-03-03 07:38:11 -05:00
eb79113e4c Update screenshots to include widgets 2016-03-03 06:49:46 -05:00
cb2f3823cd Update widget previews 2016-03-03 06:36:06 -05:00
39e29dabb8 Add code to save widget preview to file 2016-03-03 06:35:37 -05:00
51d1b93d03 Split Habit class into several smaller classes 2016-03-03 05:22:19 -05:00
8acbc63914 Move commands to their own files 2016-03-03 04:42:40 -05:00
ac8e78ff24 Minor style changes 2016-03-02 10:39:13 -05:00
162ded66d8 Improve widget measuring 2016-03-02 09:52:32 -05:00
5428209543 Improve widget colors 2016-03-01 08:37:57 -05:00
141fd30d70 Merge branch 'widgets' into dev 2016-02-29 07:45:41 -05:00
48d446a243 Minor color changes 2016-02-29 07:44:59 -05:00
ae7869d3a2 Implement multiple widget providers 2016-02-29 07:19:43 -05:00
b8cacaffa9 Refactor custom views; fix rendering issues 2016-02-29 05:50:27 -05:00
4def8f0409 Perform additional checks to avoid negative lengths 2016-02-28 15:23:20 -05:00
f0d12e9925 Widgets for HistoryView, ScoreView, etc 2016-02-28 13:55:39 -05:00
a2331260e4 Alternative design for widgets 2016-02-28 11:37:50 -05:00
c1a846d42b Minor style changes 2016-02-27 19:42:05 -05:00
031d684b3e Update main activity on notification/widget click 2016-02-27 18:26:25 -05:00
3a770e71e3 Add configuration activity for widgets 2016-02-27 18:06:57 -05:00
b29dd8ea79 Remove debug code 2016-02-27 16:31:48 -05:00
7f1553a4a1 Toggle checkmarks from widget 2016-02-27 14:09:02 -05:00
d748f5d6de Assign habits to widgets; refresh on database change 2016-02-27 13:54:24 -05:00
7234e072e6 Implement widget with fixed data 2016-02-27 13:24:01 -05:00
7f71f46367 Remove useless widget preview 2016-02-27 13:12:12 -05:00
c1dae021bf Implement dummy widget 2016-02-27 11:52:46 -05:00
88455acc76 Fix check button for previous day reminders 2016-02-27 05:44:14 -05:00
6a1cb09ca2 Remove unused imports and variables 2016-02-26 08:25:56 -05:00
33d7ab52ca Remove unused resources 2016-02-26 08:11:43 -05:00
d2682358c2 Allow custom views to be rendered on the layout editor 2016-02-26 07:44:34 -05:00
f511ca2028 Explicitly allow backups 2016-02-26 05:31:26 -05:00
d5774e8511 Close cursors 2016-02-26 05:31:17 -05:00
b6e7e72f5a Remove object allocations during draw 2016-02-26 05:29:02 -05:00
27220c9ab2 Specify locale explicitly 2016-02-26 05:17:33 -05:00
f1424e5820 Add content description for images 2016-02-26 05:16:58 -05:00
a18e0fbda0 Remove hardcoded string 2016-02-26 05:09:56 -05:00
4c3a72df81 Delete duplicate resource 2016-02-26 05:08:35 -05:00
e9ce50f686 Use correct XML namespace 2016-02-26 05:07:04 -05:00
acb26964f3 Mark as untranslatable 2016-02-26 05:06:39 -05:00
917d1218ae Update pt translation 2016-02-26 05:06:18 -05:00
f5ccd7d8c3 Move ripple backgrounds to drawable-v21 2016-02-26 04:53:19 -05:00
456a9e49a9 Remove extra translations 2016-02-26 04:52:55 -05:00
6b05004647 Implement fling and more natural scrolling on ScrollableDataView 2016-02-25 21:33:19 -05:00
f9a9339042 Use GestureDetector for scrolling 2016-02-25 21:31:28 -05:00
5e21d877c5 Fetch all the data with one call 2016-02-25 20:17:44 -05:00
af0ef90e4d Export to CSV 2016-02-25 15:46:39 -05:00
02b3ea58cf Update drag-sort-listview 2016-02-25 15:46:39 -05:00
0dcedb3c59 Update README.md 2016-02-25 10:20:25 -05:00
637d75fd2e Update README.md 2016-02-25 10:17:01 -05:00
0cd8dd973b Bump version to 1.1.1 2016-02-24 18:30:29 -05:00
2898a21157 Disallow empty list of days 2016-02-24 15:58:58 -05:00
0fa25c6701 Internationalize string 2016-02-24 15:58:45 -05:00
843f3b06a1 Update translations 2016-02-24 15:58:12 -05:00
658 changed files with 41686 additions and 5795 deletions

50
.gitignore vendored
View File

@@ -1,35 +1,23 @@
#built application files
*.apk
*.ap_
# files for the dex VM
*.dex
# Java class files
*.apk
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Windows thumbnail db
Thumbs.db
# OSX files
.DS_Store
# Eclipse project files
.classpath
.project
# Android Studio
.idea
#.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
.gradle
build/
*.dex
*.iml
*.local.*
*.swp
*.trace
*~
.DS_Store
.classpath
.gradle
.idea
.project
Thumbs.db
art/
bin/
build/
captures/
docs/
gen/
local.properties
crowdin.yaml

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "libs/drag-sort-listview"]
path = libs/drag-sort-listview
url = https://github.com/iSoron/drag-sort-listview.git

158
CHANGELOG.md Normal file
View File

@@ -0,0 +1,158 @@
# Changelog
### 1.7.2 (May 27, 2017)
* Fix crash at startup
### 1.7.1 (May 21, 2017)
* Fix crash (BadParcelableException)
* Fix layout for RTL languages such as Arabic
* Automatically detect and reject invalid database files
* Add Hebrew translation
### 1.7.0 (Mar 31, 2017)
* Sort habits automatically
* Allow swiping the header to see previous days
* Import backups directly from Google Drive or Dropbox
* Refresh data automatically at midnight
* Other minor bug fixes and enhancements
### 1.6.2 (Oct 13, 2016)
* Fix crash on Android 4.1
### 1.6.1 (Oct 10, 2016)
* Fix a crash at startup when database is corrupted
### 1.6.0 (Oct 10, 2016)
* Add option to make notifications sticky
* Add option to hide completed habits
* Display total number of repetitions for each habit
* Pebble integration: check/snooze habits from the watch
* Tasker/Locale integration: allow third-party apps to add checkmarks
* Export an unified CSV file, with checkmarks for all the habits
* Increase width of name column according to screen size
* Stop showing reminders for archived habits
* Add Danish, Dutch, Greek, Hindi and Portuguese (PT) translations
* Other minor fixes and enhancements
### 1.5.6 (Jun 19, 2016)
* Fix bug that prevented checkmark widget from working
### 1.5.5 (Jun 19, 2016)
* Fix bug that prevented check button on notification to work sometimes
* Fix bug that caused back button to apparently erase some checkmarks
* Complete French translation
* Add Croatian and Slovenian translations
### 1.5.4 (May 29, 2016)
* Fix crash upon opening settings screen in some phones
* Fix missing folders in CSV archive
* Add Serbian translation
### 1.5.3 (May 22, 2016)
* Complete Arabic and Czech translations
* Fix crash at startup
* Fix checkmark widget on custom launchers
### 1.5.2 (May 19, 2016)
* Fix missing attachment on bug reports
* Fix bug that prevents some widgets from rendering
* Complete Japanese translation
### 1.5.1 (May 17, 2016)
* Fix build on F-Droid
### 1.5.0 (May 15, 2016)
* Add night mode, with AMOLED support
* Backport material design to older devices
* Display more information on statistics screen
* Display score on main screen and checkmark widget
* Make widgets react immediately to touch
* Reschedule reminders after reboot
* Pick first day of the week according to country
* Add option to reverse order of days on main screen
* Add option to change notification sounds
* Add Catalan, Indonesian, Turkish, Ukrainian translations
* Switch between Simplified/Traditional Chinese according to country
### 1.4.1 (April 9, 2016)
* Show error message on widgets, instead of crashing
* Complete French translation
* Minor fixes to other translations
### 1.4.0 (April 7, 2016)
* Ability to import data from third-party apps
* Ability to save and restore full database backup
* Show more information on streak chart
* Simplify interface for creating habits
* Add link to Frequently Asked Questions (FAQ)
* Reduce app loading time and lag on widgets
* Generate bug reports on crash and from settings screen
* Disable vibration according to phone settings
* Add Czech translation
* Fix wrong month names for some languages
### 1.3.3 (March 20, 2016)
* Add Spanish and Korean translations
* Make small corrections to other translations
* Fix incorrect date in history calendar
### 1.3.2 (March 18, 2016)
* Add Arabic, Italian, Polish, Russian and Swedish translations
* Minor fixes to German and French translations
* Minor bug fixes
### 1.3.1 (March 15, 2016)
* Fixes crash on devices with large screen, such as the Nexus 10
* Fixes crash when clicking widgets and reminders of deleted habits
* Other minor bug fixes
### 1.3.0 (March 12, 2016)
* New frequency plot: view total repetitions per day of week
* New history editor: put checkmarks in the past
* Add German, French and Japanese translations
* Add about screen, with credits to all contributors
* Fix small bug that prevented habit from being reordered
* Fix small bug caused by rotating the device
### 1.2.0 (March 4, 2016)
* Ability to export habit data as CSV
* Widgets (checkmark, history, score and streaks)
* More natural scrolling on data views (fling)
* Minor UI improvements on pre-Lollipop devices
* Fix crash on Samsung Galaxy TabS 8.4
* Other minor bug fixes
### 1.1.1 (February 24, 2016)
* Show reminder only on chosen days of the week
* Rearrange habits by long-pressing then dragging
* Select and modify multiple habits simultaneously
* 12/24 hour format according to phone preferences
* Permanently delete habits
* Usage hints during startup
* Translation to Brazilian Portuguese and Chinese
* Other minor fixes
### 1.0.0 (February 19, 2016)
* Initial release

207
NOTICE.md Normal file
View File

@@ -0,0 +1,207 @@
# Copyright Notices
### ActiveAndroid
<https://github.com/pardom/ActiveAndroid>
Copyright (C) 2010 Michael Pardo
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
### Android Open Source Project
<https://source.android.com/>
Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
### FontAwesome
<http://fontawesome.io>
Font Awesome is a full suite of 605 pictographic icons for easy scalable
vector graphics on websites, created and maintained by Dave Gandy. Licensed
under the SIL OFL 1.1.
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
### Material Design Icons
<https://github.com/google/material-design-icons>
Material design icons are the official icon set from Google that are designed
under the material design guidelines. Available under the Creative Common
Attribution 4.0 International License (CC-BY 4.0).
### Android Flow Layout
<https://github.com/ApmeM/android-flowlayout>
Extended linear layout that wrap its content when there is no place in the current line.
Copyright 2011, Artem Votincev (apmem.org)
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
### Dagger 2
<https://github.com/google/dagger>
A fast dependency injector for Android and Java.
Copyright 2012 Square, Inc.
Copyright 2012 Google, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
### AutoFactory
<https://github.com/google/auto/tree/master/factory>
A source code generator for JSR-330-compatible factories.
Copyright 2013 Google, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
### Retrolambda
<https://github.com/orfjackal/retrolambda>
Backport of Java 8's lambda expressions to Java 7, 6 and 5
Copyright (c) 2013-2016 Esko Luontola and other Retrolambda contributors
This software is released under the Apache License 2.0.
The license text is at http://www.apache.org/licenses/LICENSE-2.0
### PebbleKit SDK
<https://github.com/pebble/pebble-android-sdk/>
Android PebbleKit SDK to talk to the Pebble via Bluetooth
The MIT License (MIT)
Copyright (c) 2014 - 2015 Pebble Technology
### AppIntro
<https://github.com/PaoloRotolo/AppIntro>
Make a cool intro for your Android app.
Copyright 2015 Paolo Rotolo
Copyright 2016 Maximilian Narr
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
### ButterKnife
<https://github.com/JakeWharton/butterknife>
Bind Android views and callbacks to fields and methods
Copyright 2013 Jake Wharton
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
### opencsv
<http://opencsv.sourceforge.net/>
Opencsv is a very simple csv (comma-separated values) parser library for Java.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

119
README.md
View File

@@ -1,16 +1,23 @@
# Loop Habit Tracker
Loop is a simple Android app that helps you create and maintain good habits. Detailed graphs and statistics show you how your habits improved over time. It is completely ad-free and open source, with no intrusive permissions. Join the open beta at [Google Play Store](https://play.google.com/apps/testing/org.isoron.uhabits).
<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>
-->
## Features
Loop is a simple Android app that helps you create and maintain good habits,
allowing you to achieve your long-term goals. Detailed graphs and statistics
show you how your habits improved over time. It is completely ad-free and open
source.
* Simple and beautiful interface, following the Material Design guidelines.
* Advanced algorithms for calculating the strength of your habits. Every repetition makes your habit stronger, and every missed day makes it weaker. A few missed days after a long streak, however, will not completely destroy your entire progress.
* Detailed graphs and statistics, showing how did you habits improve over time. Scroll back to see the complete history of your habit.
* Support for both daily habits and habits with more complex schedules, such as 3 times every week; one time every other week; or every other day.
* Habit reminders at a chosen hour of the day.
* Support for Android Wear. Reminders can be checked or dismissed from the watch.
* Completely ad-free and open source. There are absolutely no advertisements, annoying notifications or intrusive permissions in this app, and there will never be. The complete source code is available under the GPLv3.
<p align="center">
<a href="https://play.google.com/store/apps/details?id=org.isoron.uhabits&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-AC-global-none-all-co-pr-py-PartBadges-Oct1515-1"><img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/apps/en-play-badge-border.png" height="75px"/></a>
<a href="http://f-droid.org/app/org.isoron.uhabits"><img alt="Git if on F-Droid" src="http://i.imgur.com/baSPE7X.png" height="75px"/></a>
</p>
## Screenshots
@@ -18,12 +25,106 @@ Loop is a simple Android app that helps you create and maintain good habits. Det
[![Edit habit][screen2th]][screen2]
[![Habit strength][screen3th]][screen3]
[![Habit history and streaks][screen4th]][screen4]
[![Widgets][screen5th]][screen5]
[![Night mode][screen6th]][screen6]
## Features
* **Simple, beautiful and modern interface.** Loop has a minimalistic interface
that is easy to use and follows the material design guidelines.
* **Habit score.** In addition to showing your current streak, Loop has an
advanced algorithm for calculating the strength of your habits. Every
repetition makes your habit stronger, and every missed day makes it weaker. A
few missed days after a long streak, however, will not completely destroy
your entire progress.
* **Detailed graphs and statistics.** Clearly see how your habits improved over
time with beautiful and detailed graphs. Scroll back to see the complete
history of your habits.
* **Flexible schedules.** Supports both daily habits and habits with more
complex schedules, such as 3 times every week; one time every other week; or
every other day.
* **Reminders.** Create an individual reminder for each habit, at a chosen hour
of the day. Easily check, dismiss or snooze your habit directly from the
notification, without opening the app.
* **Optimized for smartwatches.** Reminders can be checked, snoozed or
dismissed directly from your Android Wear watch.
* **Completely ad-free and open source.** There are absolutely no
advertisements, annoying notifications or intrusive permissions in this app,
and there will never be. The complete source code is available under the
GPLv3.
## Installing
The easiest way to install Loop is through the [Google Play Store][playstore] or [F-Droid][fdroid].
You may also download and install the APK from the [releases page][releases];
note, however, that the app will not be updated automatically. To build this
app from the source code, see [build instructions][build].
## Contributing
Loop is an open source project developed entirely by volunteers. If you would
like to contribute to the project, you are very welcome. There are many ways to
contribute, even if you are not a software developer.
* **Report bugs, suggest features.** The easiest way to contribute is to simply
use the app and let us know if you find any problems or have any suggestions
to improve it. You can either use the link inside the app, or open an issue
at GitHub. If you would like to receive the newest versions of the app
earlier than everyone else, [join our open beta on Google Play][beta].
* **Spread the word.** If you like the app, share it with your family, friends
and colleagues. You can also rate and review the app on Google Play Store, to help
other users find it more easily.
* **Translate the app into your own language.** If you are not a native English
speaker, and would like to see the app translated into your own language,
please join our [open translation project 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
contribute with code. Please, see the [developer guidelines][dev-guide] for more details.
## License
<img align="right" src="https://www.gnu.org/graphics/gplv3-88x31.png">
Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
Loop Habit Tracker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
Loop Habit Tracker is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
[screen1]: screenshots/original/uhabits1.png
[screen2]: screenshots/original/uhabits2.png
[screen3]: screenshots/original/uhabits3.png
[screen4]: screenshots/original/uhabits4.png
[screen5]: screenshots/original/uhabits5.png
[screen6]: screenshots/original/uhabits6.png
[screen1th]: screenshots/thumbs/uhabits1.png
[screen2th]: screenshots/thumbs/uhabits2.png
[screen3th]: screenshots/thumbs/uhabits3.png
[screen4th]: screenshots/thumbs/uhabits4.png
[screen5th]: screenshots/thumbs/uhabits5.png
[screen6th]: screenshots/thumbs/uhabits6.png
[poedit]: https://poeditor.com/join/project/8DWX5pfjS0
[playstore]: https://play.google.com/store/apps/details?id=org.isoron.uhabits
[releases]: https://github.com/iSoron/uhabits/releases
[fdroid]: http://f-droid.org/app/org.isoron.uhabits
[dev-guide]: https://github.com/iSoron/uhabits/wiki/Developer-guidelines
[build]: https://github.com/iSoron/uhabits/wiki/Developer-guidelines#building
[beta]: https://play.google.com/apps/testing/org.isoron.uhabits

View File

@@ -1,13 +1,22 @@
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'jacoco'
android {
compileSdkVersion 23
buildToolsVersion "21.1.2"
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "org.isoron.uhabits"
minSdkVersion 15
targetSdkVersion 23
targetSdkVersion 25
buildConfigField "Integer", "databaseVersion", "15"
buildConfigField "String", "databaseFilename", "\"uhabits.db\""
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArgument "size", "medium"
}
buildTypes {
@@ -15,17 +24,119 @@ android {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
debug {
testCoverageEnabled = false
}
}
lintOptions {
checkReleaseBuilds false
}
compileOptions {
targetCompatibility 1.8
sourceCompatibility 1.8
}
testOptions {
unitTests.all {
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen { false }
showStandardStreams = true
}
}
}
}
dependencies {
compile 'com.android.support:support-v4:23.1.1'
androidTestApt 'com.google.dagger:dagger-compiler:2.2'
androidTestCompile 'com.android.support:support-annotations:25.3.0'
androidTestCompile 'com.android.support.test:rules:0.5'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.google.auto.factory:auto-factory:1.0-beta3'
androidTestCompile "com.google.dexmaker:dexmaker:1.2"
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
androidTestCompile 'org.mockito:mockito-core:1.10.19'
apt 'com.google.dagger:dagger-compiler:2.2'
apt 'com.jakewharton:butterknife-compiler:8.0.1'
compile 'com.android.support:appcompat-v7:25.3.0'
compile 'com.android.support:design:25.3.0'
compile 'com.android.support:preference-v14:25.3.0'
compile 'com.android.support:support-v4:25.3.0'
compile 'com.getpebble:pebblekit:3.0.0'
compile 'com.github.paolorotolo:appintro:3.4.0'
compile project(':libs:drag-sort-listview:library')
compile files('libs/ActiveAndroid.jar')
compile 'com.google.auto.factory:auto-factory:1.0-beta3'
compile 'com.google.dagger:dagger:2.2'
compile 'com.jakewharton:butterknife:8.0.1'
compile 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'
compile 'com.opencsv:opencsv:3.7'
compile 'org.apmem.tools:layouts:1.10@aar'
compile 'org.jetbrains:annotations-java5:15.0'
provided 'javax.annotation:jsr250-api:1.0'
testApt 'com.google.dagger:dagger-compiler:2.2'
testCompile 'junit:junit:4.12'
testCompile 'org.hamcrest:hamcrest-library:1.3'
testCompile 'org.mockito:mockito-core:1.10.19'
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.1') {
exclude group: 'com.android.support'
}
androidTestCompile('com.android.support.test.espresso:espresso-intents:2.2.1') {
exclude group: 'com.android.support'
}
androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.1') {
exclude group: 'com.android.support'
}
}
retrolambda {
defaultMethods true
}
jacoco {
toolVersion = "0.7.4.201502262128"
}
task coverageReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
jacocoClasspath = configurations['androidJacocoAnt']
reports {
html.enabled = true
}
def excludes = [
'**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*',
'**/*Test*.*',
'**/*$Lambda$*',
'**/*$ViewBinder*',
'**/*MembersInjector*',
'**/*_Provide*',
'**/com/android/**/*',
'android/**/*',
'**/*Dagger*',
'**/*_Factory*'
]
def srcDir = "${project.projectDir}/src/main/java"
def classDir = "${buildDir}/intermediates/classes/debug"
def jvmExecData = "${buildDir}/jacoco/testDebugUnitTest.exec"
def connectedExecData = "${buildDir}/outputs/code-coverage/connected/coverage.ec"
sourceDirectories = files(srcDir)
classDirectories = files(fileTree(dir: classDir, excludes: excludes))
executionData = files(jvmExecData, connectedExecData)
}

Binary file not shown.

3
app/proguard-rules.txt Normal file
View File

@@ -0,0 +1,3 @@
-dontwarn java.beans.**
-dontwarn java.lang.**
-dontobfuscate

View 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,
1 HabitName HabitDescription HabitCategory CalendarDate Value CommentText
2 Breed dragons with love and fire Diet & Food 2016-03-18 1
3 Breed dragons with love and fire Diet & Food 2016-03-19 1
4 Breed dragons with love and fire Diet & Food 2016-03-21 1
5 Reduce sleep only 2 hours per day Time Management 2016-03-15 1
6 Reduce sleep only 2 hours per day Time Management 2016-03-16 1
7 Reduce sleep only 2 hours per day Time Management 2016-03-17 1
8 Reduce sleep only 2 hours per day Time Management 2016-03-21 1
9 No-arms pushup Become like water my friend! Fitness 2016-03-15 1
10 No-arms pushup Become like water my friend! Fitness 2016-03-16 1
11 No-arms pushup Become like water my friend! Fitness 2016-03-18 1
12 No-arms pushup Become like water my friend! Fitness 2016-03-21 1
13 No-arms pushup Become like water my friend! Fitness 2016-03-15 1
14 No-arms pushup Become like water my friend! Fitness 2016-03-16 1
15 No-arms pushup Become like water my friend! Fitness 2016-03-18 1
16 No-arms pushup Become like water my friend! Fitness 2016-03-21 1
17 Grow spiritually transcend ego, practice compassion, smile and breath Meditation 2016-03-15 1
18 Grow spiritually transcend ego, practice compassion, smile and breath Meditation 2016-03-17 1
19 Grow spiritually transcend ego, practice compassion, smile and breath Meditation 2016-03-21 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

View 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -0,0 +1,36 @@
/*
* 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 org.isoron.uhabits.models.sqlite.*;
import org.isoron.uhabits.tasks.*;
import dagger.*;
@AppScope
@Component(modules = {
AppModule.class, SingleThreadTaskRunner.class, SQLModelFactory.class
})
public interface AndroidTestComponent extends AppComponent
{
}

View File

@@ -0,0 +1,135 @@
/*
* 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.appwidget.*;
import android.content.*;
import android.os.*;
import android.support.annotation.*;
import android.support.test.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import java.util.*;
import java.util.concurrent.*;
import static junit.framework.Assert.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
public class BaseAndroidTest
{
// 8:00am, January 25th, 2015 (UTC)
public static final long FIXED_LOCAL_TIME = 1422172800000L;
private static boolean isLooperPrepared;
protected Context testContext;
protected Context targetContext;
protected Preferences prefs;
protected HabitList habitList;
protected TaskRunner taskRunner;
protected HabitLogger logger;
protected HabitFixtures fixtures;
protected CountDownLatch latch;
protected AndroidTestComponent component;
protected ModelFactory modelFactory;
@Before
public void setUp()
{
if (!isLooperPrepared)
{
Looper.prepare();
isLooperPrepared = true;
}
targetContext = InstrumentationRegistry.getTargetContext();
testContext = InstrumentationRegistry.getContext();
DateUtils.setFixedLocalTime(FIXED_LOCAL_TIME);
setTheme(R.style.AppBaseTheme);
component = DaggerAndroidTestComponent
.builder()
.appModule(new AppModule(targetContext.getApplicationContext()))
.build();
HabitsApplication.setComponent(component);
prefs = component.getPreferences();
habitList = component.getHabitList();
taskRunner = component.getTaskRunner();
logger = component.getHabitsLogger();
modelFactory = component.getModelFactory();
fixtures = new HabitFixtures(modelFactory, habitList);
latch = new CountDownLatch(1);
}
protected void assertWidgetProviderIsInstalled(Class componentClass)
{
ComponentName provider =
new ComponentName(targetContext, componentClass);
AppWidgetManager manager = AppWidgetManager.getInstance(targetContext);
List<ComponentName> installedProviders = new LinkedList<>();
for (AppWidgetProviderInfo info : manager.getInstalledProviders())
installedProviders.add(info.provider);
assertThat(installedProviders, hasItems(provider));
}
protected void awaitLatch() throws InterruptedException
{
assertTrue(latch.await(60, TimeUnit.SECONDS));
}
protected void setTheme(@StyleRes int themeId)
{
targetContext.setTheme(themeId);
StyledResources.setFixedTheme(themeId);
}
protected void sleep(int time)
{
try
{
Thread.sleep(time);
}
catch (InterruptedException e)
{
fail();
}
}
}

View File

@@ -0,0 +1,246 @@
/*
* 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.graphics.*;
import android.os.*;
import android.support.annotation.*;
import android.view.*;
import android.widget.*;
import org.isoron.uhabits.utils.*;
import org.isoron.uhabits.widgets.*;
import java.io.*;
import static android.view.View.MeasureSpec.*;
import static junit.framework.Assert.*;
public class BaseViewTest extends BaseAndroidTest
{
protected static final double DEFAULT_SIMILARITY_CUTOFF = 0.09;
public static final int HISTOGRAM_BIN_SIZE = 8;
private double similarityCutoff;
@Override
public void setUp()
{
super.setUp();
similarityCutoff = DEFAULT_SIMILARITY_CUTOFF;
}
protected void assertRenders(View view, String expectedImagePath)
throws IOException
{
StringBuilder errorMessage = new StringBuilder();
expectedImagePath = getVersionedViewAssetPath(expectedImagePath);
if (view.isLayoutRequested()) measureView(view, view.getMeasuredWidth(),
view.getMeasuredHeight());
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))) > similarityCutoff)
{
similarEnough = false;
errorMessage.append(String.format(
"Rendered image has wrong histogram (distance=%f). ",
distance));
}
if (!similarEnough)
{
saveBitmap(expectedImagePath, ".expected", scaledExpected);
String path = saveBitmap(expectedImagePath, "", actual);
errorMessage.append(
String.format("Actual rendered image saved to %s", path));
fail(errorMessage.toString());
}
expected.recycle();
scaledExpected.recycle();
}
@NonNull
protected FrameLayout convertToView(BaseWidget widget,
int width,
int height)
{
widget.setDimensions(
new WidgetDimensions(width, height, width, height));
FrameLayout view = new FrameLayout(targetContext);
RemoteViews remoteViews = widget.getPortraitRemoteViews();
view.addView(remoteViews.apply(targetContext, view));
measureView(view, width, height);
return view;
}
protected int dpToPixels(int dp)
{
return (int) InterfaceUtils.dpToPixels(targetContext, dp);
}
protected void measureView(View view, int width, int height)
{
int specWidth = makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
int specHeight = makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
view.setLayoutParams(new ViewGroup.LayoutParams(width, height));
view.measure(specWidth, specHeight);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
}
protected void setSimilarityCutoff(double similarityCutoff)
{
this.similarityCutoff = similarityCutoff;
}
protected void skipAnimation(View view)
{
ViewPropertyAnimator animator = view.animate();
animator.setDuration(0);
animator.start();
}
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();
}
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;
}
private Bitmap getBitmapFromAssets(String path) throws IOException
{
InputStream stream = testContext.getAssets().open(path);
return BitmapFactory.decodeStream(stream);
}
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 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 = FileUtils.getSDCardDir("test-screenshots");
if (dir == null) dir = FileUtils.getFilesDir(targetContext,"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;
}
}

View File

@@ -0,0 +1,99 @@
/*
* 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 org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.DateUtils;
public class HabitFixtures
{
public boolean NON_DAILY_HABIT_CHECKS[] = {
true, false, false, true, true, true, false, false, true, true
};
private ModelFactory modelFactory;
private final HabitList habitList;
public HabitFixtures(ModelFactory modelFactory, HabitList habitList)
{
this.modelFactory = modelFactory;
this.habitList = habitList;
}
public Habit createEmptyHabit()
{
return createEmptyHabit(null);
}
public Habit createEmptyHabit(Long id)
{
Habit habit = modelFactory.buildHabit();
habit.setName("Meditate");
habit.setDescription("Did you meditate this morning?");
habit.setColor(3);
habit.setFrequency(Frequency.DAILY);
habit.setId(id);
habitList.add(habit);
return habit;
}
public Habit createLongHabit()
{
Habit habit = createEmptyHabit();
habit.setFrequency(new Frequency(3, 7));
habit.setColor(4);
long day = DateUtils.millisecondsInOneDay;
long today = DateUtils.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.getRepetitions().toggleTimestamp(today - mark * day);
return habit;
}
public Habit createShortHabit()
{
Habit habit = modelFactory.buildHabit();
habit.setName("Wake up early");
habit.setDescription("Did you wake up before 6am?");
habit.setFrequency(new Frequency(2, 3));
habitList.add(habit);
long timestamp = DateUtils.getStartOfToday();
for (boolean c : NON_DAILY_HABIT_CHECKS)
{
if (c) habit.getRepetitions().toggleTimestamp(timestamp);
timestamp -= DateUtils.millisecondsInOneDay;
}
return habit;
}
public void purgeHabits(HabitList habitList)
{
for (Habit h : habitList)
habitList.remove(h);
}
}

View 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.os.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.activities.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HabitLoggerTest extends BaseAndroidTest
{
@Test
public void testLogReminderScheduled() throws IOException
{
if (!isLogcatAvailable()) return;
long time = 1422277200000L; // 13:00 jan 26, 2015 (UTC)
Habit habit = fixtures.createEmptyHabit();
habit.setName("Write journal");
logger.logReminderScheduled(habit, time);
String expectedMsg = "Setting alarm (2015-01-26 130000): Wri\n";
assertLogcatContains(expectedMsg);
}
protected void assertLogcatContains(String expectedMsg) throws IOException
{
BaseSystem system = new BaseSystem(targetContext);
String logcat = system.getLogcat();
assertThat(logcat, containsString(expectedMsg));
}
protected boolean isLogcatAvailable()
{
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.os.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.activities.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HabitsApplicationTest extends BaseAndroidTest
{
@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();
BaseSystem system = new BaseSystem(targetContext);
String log = system.getLogcat();
assertThat(log, containsString(msg));
}
}

View File

@@ -0,0 +1,82 @@
/*
* 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.activities.common.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class FrequencyChartTest extends BaseViewTest
{
public static final String BASE_PATH = "common/FrequencyChart/";
private FrequencyChart view;
@Override
@Before
public void setUp()
{
super.setUp();
fixtures.purgeHabits(habitList);
Habit habit = fixtures.createLongHabit();
view = new FrequencyChart(targetContext);
view.setFrequency(habit.getRepetitions().getWeekdayFrequency());
view.setColor(ColorUtils.getAndroidTestColor(habit.getColor()));
measureView(view, dpToPixels(300), dpToPixels(100));
}
@Test
public void testRender() throws Throwable
{
assertRenders(view, BASE_PATH + "render.png");
}
@Test
public void testRender_withDataOffset() throws Throwable
{
view.onScroll(null, null, -dpToPixels(150), 0);
view.invalidate();
assertRenders(view, BASE_PATH + "renderDataOffset.png");
}
@Test
public void testRender_withDifferentSize() throws Throwable
{
measureView(view, dpToPixels(200), dpToPixels(200));
assertRenders(view, BASE_PATH + "renderDifferentSize.png");
}
@Test
public void testRender_withTransparentBackground() throws Throwable
{
view.setIsBackgroundTransparent(true);
assertRenders(view, BASE_PATH + "renderTransparent.png");
}
}

View File

@@ -0,0 +1,119 @@
/*
* 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.activities.common.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HistoryChartTest extends BaseViewTest
{
private static final String BASE_PATH = "common/HistoryChart/";
private HistoryChart chart;
@Override
@Before
public void setUp()
{
super.setUp();
fixtures.purgeHabits(habitList);
Habit habit = fixtures.createLongHabit();
chart = new HistoryChart(targetContext);
chart.setCheckmarks(habit.getCheckmarks().getAllValues());
chart.setColor(ColorUtils.getAndroidTestColor(habit.getColor()));
measureView(chart, dpToPixels(400), dpToPixels(200));
}
// @Test
// public void tapDate_atInvalidLocations() throws Throwable
// {
// int expectedCheckmarkValues[] = habit.getCheckmarks().getAllValues();
//
// chart.setIsEditable(true);
// tap(chart, 118, 13); // header
// tap(chart, 336, 60); // tomorrow's square
// tap(chart, 370, 60); // right axis
// waitForAsyncTasks();
//
// int actualCheckmarkValues[] = habit.getCheckmarks().getAllValues();
// assertThat(actualCheckmarkValues, equalTo(expectedCheckmarkValues));
// }
//
// @Test
// public void tapDate_withEditableView() throws Throwable
// {
// chart.setIsEditable(true);
// tap(chart, 340, 40); // today's square
// waitForAsyncTasks();
//
// long today = DateUtils.getStartOfToday();
// assertFalse(habit.getRepetitions().containsTimestamp(today));
// }
//
// @Test
// public void tapDate_withReadOnlyView() throws Throwable
// {
// chart.setIsEditable(false);
// tap(chart, 340, 40); // today's square
// waitForAsyncTasks();
//
// long today = DateUtils.getStartOfToday();
// assertTrue(habit.getRepetitions().containsTimestamp(today));
// }
@Test
public void testRender() throws Throwable
{
assertRenders(chart, BASE_PATH + "render.png");
}
@Test
public void testRender_withDataOffset() throws Throwable
{
chart.onScroll(null, null, -dpToPixels(150), 0);
chart.invalidate();
assertRenders(chart, BASE_PATH + "renderDataOffset.png");
}
@Test
public void testRender_withDifferentSize() throws Throwable
{
measureView(chart, dpToPixels(200), dpToPixels(200));
assertRenders(chart, BASE_PATH + "renderDifferentSize.png");
}
@Test
public void testRender_withTransparentBackground() throws Throwable
{
chart.setIsBackgroundTransparent(true);
assertRenders(chart, BASE_PATH + "renderTransparent.png");
}
}

View File

@@ -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.activities.common.views;
import android.graphics.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class RingViewTest extends BaseViewTest
{
private static final String BASE_PATH = "common/RingView/";
private RingView view;
@Override
@Before
public void setUp()
{
super.setUp();
view = new RingView(targetContext);
view.setPercentage(0.6f);
view.setText("60%");
view.setColor(ColorUtils.getAndroidTestColor(0));
view.setBackgroundColor(Color.WHITE);
view.setThickness(dpToPixels(3));
}
@Test
public void testRender_base() throws IOException
{
measureView(view, dpToPixels(100), dpToPixels(100));
assertRenders(view, BASE_PATH + "render.png");
}
@Test
public void testRender_withDifferentParams() throws IOException
{
view.setPercentage(0.25f);
view.setColor(ColorUtils.getAndroidTestColor(5));
measureView(view, dpToPixels(200), dpToPixels(200));
assertRenders(view, BASE_PATH + "renderDifferentParams.png");
}
}

View File

@@ -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.activities.common.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.util.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ScoreChartTest extends BaseViewTest
{
private static final String BASE_PATH = "common/ScoreChart/";
private Habit habit;
private ScoreChart view;
@Override
@Before
public void setUp()
{
super.setUp();
fixtures.purgeHabits(habitList);
habit = fixtures.createLongHabit();
view = new ScoreChart(targetContext);
view.setScores(habit.getScores().toList());
view.setColor(ColorUtils.getColor(targetContext, habit.getColor()));
view.setBucketSize(7);
measureView(view, dpToPixels(300), dpToPixels(200));
}
@Test
public void testRender() throws Throwable
{
Log.d("HabitScoreViewTest",
String.format("height=%d", dpToPixels(100)));
assertRenders(view, BASE_PATH + "render.png");
}
@Test
public void testRender_withDataOffset() throws Throwable
{
view.onScroll(null, null, -dpToPixels(150), 0);
view.invalidate();
assertRenders(view, BASE_PATH + "renderDataOffset.png");
}
@Test
public void testRender_withDifferentSize() throws Throwable
{
measureView(view, dpToPixels(200), dpToPixels(200));
assertRenders(view, BASE_PATH + "renderDifferentSize.png");
}
@Test
public void testRender_withMonthlyBucket() throws Throwable
{
view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.MONTH));
view.setBucketSize(30);
view.invalidate();
assertRenders(view, BASE_PATH + "renderMonthly.png");
}
@Test
public void testRender_withTransparentBackground() throws Throwable
{
view.setIsTransparencyEnabled(true);
assertRenders(view, BASE_PATH + "renderTransparent.png");
}
@Test
public void testRender_withYearlyBucket() throws Throwable
{
view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.YEAR));
view.setBucketSize(365);
view.invalidate();
assertRenders(view, BASE_PATH + "renderYearly.png");
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.activities.common.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class StreakChartTest extends BaseViewTest
{
private static final String BASE_PATH = "common/StreakChart/";
private StreakChart view;
@Override
@Before
public void setUp()
{
super.setUp();
fixtures.purgeHabits(habitList);
Habit habit = fixtures.createLongHabit();
view = new StreakChart(targetContext);
view.setColor(ColorUtils.getAndroidTestColor(habit.getColor()));
view.setStreaks(habit.getStreaks().getBest(5));
measureView(view, dpToPixels(300), dpToPixels(100));
}
@Test
public void testRender() throws Throwable
{
assertRenders(view, BASE_PATH + "render.png");
}
@Test
public void testRender_withSmallSize() throws Throwable
{
measureView(view, dpToPixels(100), dpToPixels(100));
assertRenders(view, BASE_PATH + "renderSmallSize.png");
}
@Test
public void testRender_withTransparentBackground() throws Throwable
{
view.setIsBackgroundTransparent(true);
assertRenders(view, BASE_PATH + "renderTransparent.png");
}
}

View File

@@ -0,0 +1,187 @@
/*
* 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.activities.habits.list.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
import java.util.concurrent.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class CheckmarkButtonViewTest extends BaseViewTest
{
public static final String PATH = "habits/list/CheckmarkButtonView/";
private CountDownLatch latch;
private CheckmarkButtonView view;
@Override
@Before
public void setUp()
{
super.setUp();
setSimilarityCutoff(0.015f);
latch = new CountDownLatch(1);
view = new CheckmarkButtonView(targetContext);
view.setValue(Checkmark.UNCHECKED);
view.setColor(ColorUtils.getAndroidTestColor(5));
measureView(view, dpToPixels(40), dpToPixels(40));
}
@Test
public void testRender_explicitCheck() throws Exception
{
view.setValue(Checkmark.CHECKED_EXPLICITLY);
assertRendersCheckedExplicitly();
}
@Test
public void testRender_implicitCheck() throws Exception
{
view.setValue(Checkmark.CHECKED_IMPLICITLY);
assertRendersCheckedImplicitly();
}
@Test
public void testRender_unchecked() throws Exception
{
view.setValue(Checkmark.UNCHECKED);
assertRendersUnchecked();
}
protected void assertRendersCheckedExplicitly() throws IOException
{
assertRenders(view, PATH + "render_explicit_check.png");
}
protected void assertRendersCheckedImplicitly() throws IOException
{
assertRenders(view, PATH + "render_implicit_check.png");
}
protected void assertRendersUnchecked() throws IOException
{
assertRenders(view, PATH + "render_unchecked.png");
}
// @Test
// public void testLongClick() throws Exception
// {
// setOnToggleListener();
// view.performLongClick();
// waitForLatch();
// assertRendersCheckedExplicitly();
// }
//
// @Test
// public void testClick_withShortToggle_fromUnchecked() throws Exception
// {
// Preferences.getInstance().setShortToggleEnabled(true);
// view.setValue(Checkmark.UNCHECKED);
// setOnToggleListenerAndPerformClick();
// assertRendersCheckedExplicitly();
// }
//
// @Test
// public void testClick_withShortToggle_fromChecked() throws Exception
// {
// Preferences.getInstance().setShortToggleEnabled(true);
// view.setValue(Checkmark.CHECKED_EXPLICITLY);
// setOnToggleListenerAndPerformClick();
// assertRendersUnchecked();
// }
//
// @Test
// public void testClick_withShortToggle_withoutListener() throws Exception
// {
// Preferences.getInstance().setShortToggleEnabled(true);
// view.setValue(Checkmark.CHECKED_EXPLICITLY);
// view.setController(null);
// view.performClick();
// assertRendersUnchecked();
// }
//
// protected void setOnToggleListenerAndPerformClick() throws InterruptedException
// {
// setOnToggleListener();
// view.performClick();
// waitForLatch();
// }
//
// @Test
// public void testClick_withoutShortToggle() throws Exception
// {
// Preferences.getInstance().setShortToggleEnabled(false);
// setOnInvalidToggleListener();
// view.performClick();
// waitForLatch();
// assertRendersUnchecked();
// }
// protected void setOnInvalidToggleListener()
// {
// view.setController(new CheckmarkButtonView.Controller()
// {
// @Override
// public void onToggleCheckmark(CheckmarkButtonView view, long timestamp)
// {
// fail();
// }
//
// @Override
// public void onInvalidToggle(CheckmarkButtonView v)
// {
// assertThat(v, equalTo(view));
// latch.countDown();
// }
// });
// }
// protected void setOnToggleListener()
// {
// view.setController(new CheckmarkButtonView.Controller()
// {
// @Override
// public void onToggleCheckmark(CheckmarkButtonView v, long t)
// {
// assertThat(v, equalTo(view));
// assertThat(t, equalTo(DateUtils.getStartOfToday()));
// latch.countDown();
// }
//
// @Override
// public void onInvalidToggle(CheckmarkButtonView view)
// {
// fail();
// }
// });
// }
}

View File

@@ -0,0 +1,98 @@
/*
* 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.activities.habits.list.views;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.models.Checkmark;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.BaseViewTest;
import org.isoron.uhabits.utils.ColorUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class CheckmarkPanelViewTest extends BaseViewTest
{
public static final String PATH = "habits/list/CheckmarkPanelView/";
private CountDownLatch latch;
private CheckmarkPanelView view;
private int checkmarks[];
@Override
@Before
public void setUp()
{
super.setUp();
setSimilarityCutoff(0.03f);
prefs.setShouldReverseCheckmarks(false);
Habit habit = fixtures.createEmptyHabit();
latch = new CountDownLatch(1);
checkmarks = new int[]{
Checkmark.CHECKED_EXPLICITLY, Checkmark.UNCHECKED,
Checkmark.CHECKED_IMPLICITLY, Checkmark.CHECKED_EXPLICITLY};
view = new CheckmarkPanelView(targetContext);
view.setHabit(habit);
view.setCheckmarkValues(checkmarks);
view.setButtonCount(4);
view.setColor(ColorUtils.getAndroidTestColor(7));
measureView(view, dpToPixels(200), dpToPixels(200));
}
// protected void waitForLatch() throws InterruptedException
// {
// assertTrue("Latch timeout", latch.await(1, TimeUnit.SECONDS));
// }
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
// @Test
// public void testToggleCheckmark_withLeftToRight() throws Exception
// {
// setToggleListener();
// view.getButton(1).performToggle();
// waitForLatch();
// }
//
// @Test
// public void testToggleCheckmark_withReverseCheckmarks() throws Exception
// {
// prefs.setShouldReverseCheckmarks(true);
// view.setCheckmarkValues(checkmarks); // refresh after preference change
//
// setToggleListener();
// view.getButton(2).performToggle();
// waitForLatch();
// }
}

View File

@@ -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.activities.habits.list.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import static org.mockito.Mockito.mock;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HabitCardViewTest extends BaseViewTest
{
private HabitCardView view;
public static final String PATH = "habits/list/HabitCardView/";
private HabitCardView.Controller controller;
private Habit habit;
@Override
public void setUp()
{
super.setUp();
setTheme(R.style.AppBaseTheme);
habit = fixtures.createLongHabit();
CheckmarkList checkmarks = habit.getCheckmarks();
long today = DateUtils.getStartOfToday();
long day = DateUtils.millisecondsInOneDay;
int[] values = checkmarks.getValues(today - 5 * day, today);
controller = mock(HabitCardView.Controller.class);
view = new HabitCardView(targetContext);
view.setHabit(habit);
view.setCheckmarkValues(values);
view.setSelected(false);
view.setScore(habit.getScores().getTodayValue());
view.setController(controller);
measureView(view, dpToPixels(400), dpToPixels(50));
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
@Test
public void testRender_selected() throws Exception
{
view.setSelected(true);
measureView(view, dpToPixels(400), dpToPixels(50));
assertRenders(view, PATH + "render_selected.png");
}
@Test
public void testChangeModel() throws Exception
{
habit.setName("Wake up early");
habit.setColor(2);
habit.getObservable().notifyListeners();
assertRenders(view, PATH + "render_changed.png");
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.activities.habits.list.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.junit.*;
import org.junit.runner.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HintViewTest extends BaseViewTest
{
public static final String PATH = "habits/list/HintView/";
private HintView view;
private HintList list;
@Before
@Override
public void setUp()
{
super.setUp();
view = new HintView(targetContext);
list = mock(HintList.class);
view.setHints(list);
measureView(view, 400, 200);
String text =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
when(list.shouldShow()).thenReturn(true);
when(list.pop()).thenReturn(text);
view.showNext();
skipAnimation(view);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
@Test
public void testClick() throws Exception
{
assertThat(view.getAlpha(), equalTo(1f));
view.performClick();
skipAnimation(view);
assertThat(view.getAlpha(), equalTo(0f));
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.activities.habits.show.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.view.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class FrequencyCardTest extends BaseViewTest
{
public static final String PATH = "habits/show/FrequencyCard/";
private FrequencyCard view;
private Habit habit;
@Before
@Override
public void setUp()
{
super.setUp();
habit = fixtures.createLongHabit();
view = (FrequencyCard) LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById(R.id.frequencyCard);
view.setHabit(habit);
view.refreshData();
measureView(view, 800, 600);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.activities.habits.show.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.view.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HistoryCardTest extends BaseViewTest
{
public static final String PATH = "habits/show/HistoryCard/";
private HistoryCard view;
private Habit habit;
@Before
@Override
public void setUp()
{
super.setUp();
habit = fixtures.createLongHabit();
view = (HistoryCard) LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById(R.id.historyCard);
view.setHabit(habit);
view.refreshData();
measureView(view, 800, 600);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.activities.habits.show.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.view.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class OverviewCardTest extends BaseViewTest
{
public static final String PATH = "habits/show/OverviewCard/";
private OverviewCard view;
private Habit habit;
@Before
@Override
public void setUp()
{
super.setUp();
habit = fixtures.createLongHabit();
view = (OverviewCard) LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById(R.id.overviewCard);
view.setHabit(habit);
view.refreshData();
measureView(view, 800, 300);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.activities.habits.show.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.view.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ScoreCardTest extends BaseViewTest
{
public static final String PATH = "habits/show/ScoreCard/";
private ScoreCard view;
private Habit habit;
@Before
@Override
public void setUp()
{
super.setUp();
habit = fixtures.createLongHabit();
view = (ScoreCard) LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById(R.id.scoreCard);
view.setHabit(habit);
view.refreshData();
measureView(view, 800, 600);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.activities.habits.show.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.view.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class StreakCardTest extends BaseViewTest
{
public static final String PATH = "habits/show/StreakCard/";
private StreakCard view;
private Habit habit;
@Before
@Override
public void setUp()
{
super.setUp();
habit = fixtures.createLongHabit();
view = (StreakCard) LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById(R.id.streakCard);
view.setHabit(habit);
view.refreshData();
measureView(view, 800, 600);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

View 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.activities.habits.show.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.view.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class SubtitleCardTest extends BaseViewTest
{
public static final String PATH = "habits/show/SubtitleCard/";
private SubtitleCard view;
private Habit habit;
@Before
@Override
public void setUp()
{
super.setUp();
habit = fixtures.createLongHabit();
habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY));
view = (SubtitleCard) LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById(R.id.subtitleCard);
view.setHabit(habit);
view.refreshData();
measureView(view, 800, 200);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.espresso;
import android.preference.Preference;
import android.view.View;
import android.widget.Adapter;
import android.widget.AdapterView;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.isoron.uhabits.models.Habit;
public class HabitMatchers
{
public static Matcher<Habit> withName(final String name)
{
return new TypeSafeMatcher<Habit>()
{
@Override
public boolean matchesSafely(Habit habit)
{
return habit.getName().equals(name);
}
@Override
public void describeTo(Description description)
{
description.appendText("name should be ").appendText(name);
}
@Override
public void describeMismatchSafely(Habit habit, Description description)
{
description.appendText("was ").appendText(habit.getName());
}
};
}
public static Matcher<View> containsHabit(final Matcher<Habit> matcher)
{
return new TypeSafeMatcher<View>()
{
@Override
protected boolean matchesSafely(View view)
{
Adapter adapter = ((AdapterView) view).getAdapter();
for (int i = 0; i < adapter.getCount(); i++)
if (matcher.matches(adapter.getItem(i))) return true;
return false;
}
@Override
public void describeTo(Description description)
{
description.appendText("with class name: ");
matcher.describeTo(description);
}
};
}
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));
}
};
}
}

View File

@@ -0,0 +1,131 @@
/*
* 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.espresso;
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.action.CoordinatesProvider;
import android.support.test.espresso.action.GeneralClickAction;
import android.support.test.espresso.action.GeneralLocation;
import android.support.test.espresso.action.Press;
import android.support.test.espresso.action.Tap;
import android.support.test.espresso.matcher.ViewMatchers;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.hamcrest.Matcher;
import org.isoron.uhabits.R;
import java.security.InvalidParameterException;
import java.util.Random;
public class HabitViewActions
{
public static ViewAction toggleAllCheckmarks()
{
final GeneralClickAction clickAction =
new GeneralClickAction(Tap.LONG, GeneralLocation.CENTER, Press.FINGER);
return new ViewAction()
{
@Override
public Matcher<View> getConstraints()
{
return ViewMatchers.isDisplayed();
}
@Override
public String getDescription()
{
return "toggleAllCheckmarks";
}
@Override
public void perform(UiController uiController, View view)
{
if (view.getId() != R.id.checkmarkPanel)
throw new InvalidParameterException("View must have id llButtons");
LinearLayout llButtons = (LinearLayout) view;
int count = llButtons.getChildCount();
for (int i = 0; i < count; i++)
{
TextView tvButton = (TextView) llButtons.getChildAt(i);
clickAction.perform(uiController, tvButton);
}
}
};
}
public static ViewAction clickAt(final int x, final int y)
{
return new GeneralClickAction(Tap.SINGLE, new CoordinatesProvider()
{
@Override
public float[] calculateCoordinates(View view)
{
int[] locations = new int[2];
view.getLocationOnScreen(locations);
final float locationX = locations[0] + x;
final float locationY = locations[1] + y;
return new float[]{locationX, locationY};
}
}, Press.FINGER);
}
public static ViewAction clickAtRandomLocations(final int count)
{
return new ViewAction()
{
@Override
public Matcher<View> getConstraints()
{
return ViewMatchers.isDisplayed();
}
@Override
public String getDescription()
{
return "clickAtRandomLocations";
}
@Override
public void perform(UiController uiController, View view)
{
int width = view.getWidth();
int height = view.getHeight();
Random random = new Random();
for(int i = 0; i < count; i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
ViewAction action = clickAt(x, y);
action.perform(uiController, view);
}
}
};
}
}

View File

@@ -0,0 +1,199 @@
/*
* 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.espresso;
import android.support.test.espresso.*;
import android.support.test.espresso.contrib.*;
import org.hamcrest.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.*;
import java.util.*;
import static android.support.test.espresso.Espresso.*;
import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.*;
import static android.support.test.espresso.assertion.ViewAssertions.*;
import static android.support.test.espresso.matcher.RootMatchers.*;
import static android.support.test.espresso.matcher.ViewMatchers.Visibility.*;
import static android.support.test.espresso.matcher.ViewMatchers.*;
import static org.hamcrest.Matchers.*;
public class MainActivityActions
{
public static String addHabit()
{
return addHabit(false);
}
public static String addHabit(boolean openDialogs)
{
String name = "New Habit " + new Random().nextInt(1000000);
String description = "Did you perform your new habit today?";
String num = "4";
String den = "8";
onView(withId(R.id.actionAdd)).perform(click());
typeHabitData(name, description, num, den);
if (openDialogs)
{
onView(withId(R.id.buttonPickColor)).perform(click());
pressBack();
onView(withId(R.id.tvReminderTime)).perform(click());
onView(withText("Done")).perform(click());
onView(withId(R.id.tvReminderDays)).perform(click());
onView(withText("OK")).perform(click());
}
onView(withId(R.id.buttonSave)).perform(click());
onData(Matchers.allOf(is(instanceOf(Habit.class)),
HabitMatchers.withName(name))).onChildView(withId(R.id.label));
return name;
}
public static void assertHabitExists(String name)
{
List<String> names = new LinkedList<>();
names.add(name);
assertHabitsExist(names);
}
public static void assertHabitsDontExist(List<String> names)
{
for (String name : names)
onView(withId(R.id.listView)).check(matches(Matchers.not(
HabitMatchers.containsHabit(HabitMatchers.withName(name)))));
}
public static void assertHabitsExist(List<String> names)
{
for (String name : names)
onData(Matchers.allOf(is(instanceOf(Habit.class)),
HabitMatchers.withName(name))).check(matches(isDisplayed()));
}
private static void clickHiddenMenuItem(int stringId)
{
try
{
// Try the ActionMode overflow menu first
onView(allOf(withContentDescription("More options"), withParent(
withParent(withClassName(containsString("Action")))))).perform(
click());
}
catch (Exception e1)
{
// Try the toolbar overflow menu
onView(allOf(withContentDescription("More options"), withParent(
withParent(withClassName(containsString("Toolbar")))))).perform(
click());
}
onView(withText(stringId)).perform(click());
}
public static void clickMenuItem(int stringId)
{
try
{
onView(withText(stringId)).perform(click());
}
catch (Exception e1)
{
try
{
onView(withContentDescription(stringId)).perform(click());
}
catch (Exception e2)
{
clickHiddenMenuItem(stringId);
}
}
}
public static void clickSettingsItem(String text)
{
onView(withClassName(containsString("RecyclerView"))).perform(
RecyclerViewActions.actionOnItem(
hasDescendant(withText(containsString(text))), click()));
}
public static void deleteHabit(String name)
{
deleteHabits(Collections.singletonList(name));
}
public static void deleteHabits(List<String> names)
{
selectHabits(names);
clickMenuItem(R.string.delete);
onView(withText("OK")).perform(click());
assertHabitsDontExist(names);
}
public static void selectHabit(String name)
{
selectHabits(Collections.singletonList(name));
}
public static void selectHabits(List<String> names)
{
boolean first = true;
for (String name : names)
{
onData(Matchers.allOf(is(instanceOf(Habit.class)),
HabitMatchers.withName(name)))
.onChildView(withId(R.id.label))
.perform(first ? longClick() : click());
first = false;
}
}
public static void typeHabitData(String name,
String description,
String num,
String den)
{
onView(withId(R.id.tvName)).perform(replaceText(name));
onView(withId(R.id.tvDescription)).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.tvFreqNum)).perform(replaceText(num));
onView(withId(R.id.tvFreqDen)).perform(replaceText(den));
}
}

View File

@@ -0,0 +1,317 @@
/*
* 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.espresso;
import android.app.*;
import android.content.*;
import android.support.test.*;
import android.support.test.espresso.*;
import android.support.test.espresso.intent.rule.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.hamcrest.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.habits.list.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import java.util.*;
import static android.support.test.espresso.Espresso.*;
import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.*;
import static android.support.test.espresso.assertion.ViewAssertions.*;
import static android.support.test.espresso.intent.Intents.*;
import static android.support.test.espresso.intent.matcher.IntentMatchers.*;
import static android.support.test.espresso.matcher.ViewMatchers.*;
import static org.hamcrest.Matchers.*;
import static org.isoron.uhabits.espresso.HabitViewActions.*;
import static org.isoron.uhabits.espresso.MainActivityActions.*;
import static org.isoron.uhabits.espresso.ShowHabitActivityActions.*;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainTest
{
private SystemHelper sys;
@Rule
public IntentsTestRule<ListHabitsActivity> activityRule =
new IntentsTestRule<>(ListHabitsActivity.class);
@Before
public void setup()
{
Context context =
InstrumentationRegistry.getInstrumentation().getContext();
sys = new SystemHelper(context);
sys.disableAllAnimations();
sys.acquireWakeLock();
sys.unlockScreen();
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();
}
public void skipTutorial()
{
try
{
for (int i = 0; i < 10; i++)
onView(allOf(withClassName(endsWith("AppCompatImageButton")),
isDisplayed())).perform(click());
}
catch (NoMatchingViewException e)
{
// ignored
}
}
@After
public void tearDown()
{
sys.releaseWakeLock();
}
/**
* User opens menu, clicks about, sees about screen.
*/
@Test
public void testAbout()
{
clickMenuItem(R.string.about);
onView(isRoot()).perform(swipeUp());
}
/**
* 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(Matchers.allOf(is(instanceOf(Habit.class)),
HabitMatchers.withName(name)))
.onChildView(withId(R.id.checkmarkPanel))
.perform(toggleAllCheckmarks());
Thread.sleep(1200);
onData(Matchers.allOf(is(instanceOf(Habit.class)),
HabitMatchers.withName(name)))
.onChildView(withId(R.id.label))
.perform(click());
onView(withId(R.id.scoreView)).perform(scrollTo(), swipeRight());
onView(withId(R.id.frequencyChart)).perform(scrollTo(), swipeRight());
}
/**
* 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.actionAdd)).perform(click());
typeHabitData("", "", "15", "7");
onView(withId(R.id.buttonSave)).perform(click());
onView(withId(R.id.tvName)).check(matches(isDisplayed()));
}
/**
* 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);
clickMenuItem(R.string.archive);
assertHabitsDontExist(names);
clickMenuItem(R.string.show_archived);
assertHabitsExist(names);
selectHabits(names);
clickMenuItem(R.string.unarchive);
clickMenuItem(R.string.show_archived);
assertHabitsExist(names);
deleteHabits(names);
}
/**
* 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(Matchers.allOf(is(instanceOf(Habit.class)),
HabitMatchers.withName(name)))
.onChildView(withId(R.id.label))
.perform(longClick());
clickMenuItem(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);
clickMenuItem(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(Matchers.allOf(is(instanceOf(Habit.class)),
HabitMatchers.withName(name)))
.onChildView(withId(R.id.label))
.perform(click());
openHistoryEditor();
onView(withClassName(endsWith("HabitHistoryView"))).perform(
clickAtRandomLocations(20));
pressBack();
onView(withId(R.id.historyChart)).perform(scrollTo(), swipeRight(),
swipeLeft());
}
/**
* User creates a habit, opens settings, clicks export as CSV, is asked what
* activity should handle the file.
*/
@Test
public void testExportCSV()
{
addHabit();
clickMenuItem(R.string.settings);
clickSettingsItem("Export as CSV");
intended(hasAction(Intent.ACTION_SEND));
}
/**
* 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();
clickMenuItem(R.string.settings);
String date =
DateFormats.getBackupDateFormat().format(DateUtils.getLocalTime());
date = date.substring(0, date.length() - 2);
clickSettingsItem("Export full backup");
intended(hasAction(Intent.ACTION_SEND));
deleteHabit(name);
clickMenuItem(R.string.settings);
clickSettingsItem("Import data");
onData(
allOf(is(instanceOf(String.class)), startsWith("Backups"))).perform(
click());
onData(
allOf(is(instanceOf(String.class)), containsString(date))).perform(
click());
selectHabit(name);
}
/**
* User opens the settings and generates a bug report.
*/
@Test
public void testGenerateBugReport()
{
clickMenuItem(R.string.settings);
clickSettingsItem("Generate bug report");
intended(hasAction(Intent.ACTION_SEND));
}
/**
* User opens menu, clicks Help, sees website.
*/
@Test
public void testHelp()
{
clickMenuItem(R.string.help);
intended(hasAction(Intent.ACTION_VIEW));
}
/**
* User opens menu, clicks settings, sees settings screen.
*/
@Test
public void testSettings()
{
clickMenuItem(R.string.settings);
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.espresso;
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.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.scrollTo;
public class ShowHabitActivityActions
{
public static void openHistoryEditor()
{
onView(ViewMatchers.withId(R.id.edit))
.perform(scrollTo(), click());
}
}

View File

@@ -0,0 +1,124 @@
/*
* 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.espresso;
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 + " :'(");
}
}
}

View File

@@ -0,0 +1,119 @@
/*
* 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.io;
import android.content.*;
import android.support.test.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import static junit.framework.Assert.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HabitsCSVExporterTest extends BaseAndroidTest
{
private File baseDir;
@Before
public void setUp()
{
super.setUp();
fixtures.purgeHabits(habitList);
fixtures.createShortHabit();
fixtures.createEmptyHabit();
Context targetContext = InstrumentationRegistry.getTargetContext();
baseDir = targetContext.getCacheDir();
}
@Test
public void testExportCSV() throws IOException
{
List<Habit> selected = new LinkedList<>();
for (Habit h : habitList) selected.add(h);
HabitsCSVExporter exporter =
new HabitsCSVExporter(habitList, selected, 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");
assertPathExists("Checkmarks.csv");
assertPathExists("Scores.csv");
}
private void assertAbsolutePathExists(String s)
{
File file = new File(s);
assertTrue(
String.format("File %s should exist", file.getAbsolutePath()),
file.exists());
}
private void assertPathExists(String s)
{
assertAbsolutePathExists(
String.format("%s/%s", baseDir.getAbsolutePath(), s));
}
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();
FileUtils.copy(stream, outputFile);
}
zip.close();
}
}

View File

@@ -0,0 +1,159 @@
/*
* 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.io;
import android.content.*;
import android.support.test.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
import java.util.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ImportTest extends BaseAndroidTest
{
private Context context;
@Override
@Before
public void setUp()
{
super.setUp();
DateUtils.setFixedLocalTime(null);
fixtures.purgeHabits(habitList);
context = InstrumentationRegistry.getInstrumentation().getContext();
}
@Test
public void testHabitBullCSV() throws IOException
{
importFromFile("habitbull.csv");
assertThat(habitList.size(), equalTo(4));
Habit habit = habitList.getByPosition(0);
assertThat(habit.getName(), equalTo("Breed dragons"));
assertThat(habit.getDescription(), equalTo("with love and fire"));
assertThat(habit.getFrequency(), equalTo(Frequency.DAILY));
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");
assertThat(habitList.size(), equalTo(9));
Habit habit = habitList.getByPosition(0);
assertThat(habit.getName(), equalTo("Wake up early"));
assertThat(habit.getFrequency(),
equalTo(Frequency.THREE_TIMES_PER_WEEK));
assertTrue(containsRepetition(habit, 2016, 3, 14));
assertTrue(containsRepetition(habit, 2016, 3, 16));
assertFalse(containsRepetition(habit, 2016, 3, 17));
}
@Test
public void testRewireDB() throws IOException
{
importFromFile("rewire.db");
assertThat(habitList.size(), equalTo(3));
Habit habit = habitList.getByPosition(0);
assertThat(habit.getName(), equalTo("Wake up early"));
assertThat(habit.getFrequency(),
equalTo(Frequency.THREE_TIMES_PER_WEEK));
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 = habitList.getByPosition(1);
assertThat(habit.getName(), equalTo("brush teeth"));
assertThat(habit.getFrequency(),
equalTo(Frequency.THREE_TIMES_PER_WEEK));
assertThat(habit.hasReminder(), equalTo(true));
Reminder reminder = habit.getReminder();
assertThat(reminder.getHour(), equalTo(8));
assertThat(reminder.getMinute(), equalTo(0));
boolean[] reminderDays = { false, true, true, true, true, true, false };
assertThat(reminder.getDays().toArray(), equalTo(reminderDays));
}
@Test
public void testTickmateDB() throws IOException
{
importFromFile("tickmate.db");
assertThat(habitList.size(), equalTo(3));
Habit h = habitList.getByPosition(0);
assertThat(h.getName(), 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));
}
private boolean containsRepetition(Habit h, int year, int month, int day)
{
GregorianCalendar date = DateUtils.getStartOfTodayCalendar();
date.set(year, month - 1, day);
return h.getRepetitions().containsTimestamp(date.getTimeInMillis());
}
private void copyAssetToFile(String assetPath, File dst) throws IOException
{
InputStream in = context.getAssets().open(assetPath);
FileUtils.copy(in, dst);
}
private void importFromFile(String assetFilename) throws IOException
{
File file = File.createTempFile("asset", "");
copyAssetToFile(assetFilename, file);
assertTrue(file.exists());
assertTrue(file.canRead());
GenericImporter importer = component.getGenericImporter();
assertThat(importer.canHandle(file), is(true));
importer.importHabitsFromFile(file);
file.delete();
}
}

View File

@@ -0,0 +1,240 @@
/*
* 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.models;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.hamcrest.*;
import org.isoron.uhabits.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
import java.util.*;
import static junit.framework.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.isoron.uhabits.models.HabitList.Order.*;
@SuppressWarnings("JavaDoc")
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HabitListTest extends BaseAndroidTest
{
private ArrayList<Habit> habitsArray;
private HabitList activeHabits;
private HabitList reminderHabits;
@Override
public void setUp()
{
super.setUp();
habitList.removeAll();
habitsArray = new ArrayList<>();
for (int i = 0; i < 10; i++)
{
Habit habit = fixtures.createEmptyHabit((long) i);
habitsArray.add(habit);
if (i % 3 == 0)
habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY));
habitList.update(habit);
}
habitsArray.get(0).setArchived(true);
habitsArray.get(1).setArchived(true);
habitsArray.get(4).setArchived(true);
habitsArray.get(7).setArchived(true);
activeHabits = habitList.getFiltered(new HabitMatcherBuilder().build());
reminderHabits = habitList.getFiltered(new HabitMatcherBuilder()
.setArchivedAllowed(true)
.setReminderRequired(true)
.build());
}
@Test
public void test_size()
{
assertThat(habitList.size(), equalTo(10));
}
@Test
public void test_countActive()
{
assertThat(activeHabits.size(), equalTo(6));
}
@Test
public void test_getByPosition()
{
assertThat(habitList.getByPosition(0), equalTo(habitsArray.get(0)));
assertThat(habitList.getByPosition(3), equalTo(habitsArray.get(3)));
assertThat(habitList.getByPosition(9), equalTo(habitsArray.get(9)));
assertThat(activeHabits.getByPosition(0), equalTo(habitsArray.get(2)));
}
@Test
public void test_getHabitsWithReminder()
{
assertThat(reminderHabits.size(), equalTo(4));
assertThat(reminderHabits.getByPosition(1),
equalTo(habitsArray.get(3)));
}
@Test
public void test_get_withInvalidId()
{
assertThat(habitList.getById(100L), is(nullValue()));
}
@Test
public void test_get_withValidId()
{
Habit habit1 = habitsArray.get(0);
Habit habit2 = habitList.getById(habit1.getId());
assertThat(habit1, equalTo(habit2));
}
@Test
public void test_reorder()
{
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 = habitList.getByPosition(from);
Habit toHabit = habitList.getByPosition(to);
habitList.reorder(fromHabit, toHabit);
int actualPositions[] = new int[10];
for (int j = 0; j < 10; j++)
{
Habit h = habitList.getById(j);
assertNotNull(h);
actualPositions[j] = habitList.indexOf(h);
}
assertThat(actualPositions, equalTo(expectedPosition[i]));
}
}
@Test
public void test_writeCSV() throws IOException
{
habitList.removeAll();
Habit h1 = fixtures.createEmptyHabit();
h1.setName("Meditate");
h1.setDescription("Did you meditate this morning?");
h1.setFrequency(Frequency.DAILY);
h1.setColor(3);
Habit h2 = fixtures.createEmptyHabit();
h2.setName("Wake up early");
h2.setDescription("Did you wake up before 6am?");
h2.setFrequency(new Frequency(2, 3));
h2.setColor(5);
habitList.update(h1);
habitList.update(h2);
String expectedCSV =
"Position,Name,Description,NumRepetitions,Interval,Color\n" +
"001,Meditate,Did you meditate this morning?,1,1,#AFB42B\n" +
"002,Wake up early,Did you wake up before 6am?,2,3,#00897B\n";
StringWriter writer = new StringWriter();
habitList.writeCSV(writer);
MatcherAssert.assertThat(writer.toString(), equalTo(expectedCSV));
}
@Test
public void test_ordering()
{
habitList.removeAll();
Habit h3 = fixtures.createEmptyHabit();
h3.setName("C Habit");
h3.setColor(0);
habitList.update(h3);
Habit h1 = fixtures.createEmptyHabit();
h1.setName("A Habit");
h1.setColor(2);
habitList.update(h1);
Habit h4 = fixtures.createEmptyHabit();
h4.setName("D Habit");
h4.setColor(1);
habitList.update(h4);
Habit h2 = fixtures.createEmptyHabit();
h2.setName("B Habit");
h2.setColor(2);
habitList.update(h2);
habitList.setOrder(BY_POSITION);
assertThat(habitList.getByPosition(0), equalTo(h3));
assertThat(habitList.getByPosition(1), equalTo(h1));
assertThat(habitList.getByPosition(2), equalTo(h4));
assertThat(habitList.getByPosition(3), equalTo(h2));
habitList.setOrder(BY_NAME);
assertThat(habitList.getByPosition(0), equalTo(h1));
assertThat(habitList.getByPosition(1), equalTo(h2));
assertThat(habitList.getByPosition(2), equalTo(h3));
assertThat(habitList.getByPosition(3), equalTo(h4));
habitList.remove(h1);
habitList.add(h1);
assertThat(habitList.getByPosition(0), equalTo(h1));
habitList.setOrder(BY_COLOR);
assertThat(habitList.getByPosition(0), equalTo(h3));
assertThat(habitList.getByPosition(1), equalTo(h4));
assertThat(habitList.getByPosition(2), equalTo(h1));
assertThat(habitList.getByPosition(3), equalTo(h2));
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.models.sqlite;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.models.sqlite.records.*;
import org.junit.*;
import org.junit.runner.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HabitRecordTest extends BaseAndroidTest
{
@Override
public void setUp()
{
super.setUp();
Habit h = component.getModelFactory().buildHabit();
h.setName("Hello world");
h.setId(1000L);
HabitRecord record = new HabitRecord();
record.copyFrom(h);
record.position = 0;
record.save(1000L);
}
@Test
public void testCopyFrom()
{
Habit habit = component.getModelFactory().buildHabit();
habit.setName("Hello world");
habit.setDescription("Did you greet the world today?");
habit.setColor(1);
habit.setArchived(true);
habit.setFrequency(Frequency.THREE_TIMES_PER_WEEK);
habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY));
habit.setId(1000L);
HabitRecord rec = new HabitRecord();
rec.copyFrom(habit);
assertThat(rec.name, equalTo(habit.getName()));
assertThat(rec.description, equalTo(habit.getDescription()));
assertThat(rec.color, equalTo(habit.getColor()));
assertThat(rec.archived, equalTo(1));
assertThat(rec.freqDen, equalTo(7));
assertThat(rec.freqNum, equalTo(3));
Reminder reminder = habit.getReminder();
assertThat(rec.reminderDays, equalTo(reminder.getDays().toInteger()));
assertThat(rec.reminderHour, equalTo(reminder.getHour()));
assertThat(rec.reminderMin, equalTo(reminder.getMinute()));
habit.setReminder(null);
rec.copyFrom(habit);
assertThat(rec.reminderMin, equalTo(null));
assertThat(rec.reminderHour, equalTo(null));
assertThat(rec.reminderDays, equalTo(0));
}
}

View File

@@ -0,0 +1,127 @@
/*
* 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.models.sqlite;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import com.activeandroid.query.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.models.sqlite.records.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import java.util.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class SQLiteCheckmarkListTest extends BaseAndroidTest
{
private Habit habit;
private CheckmarkList checkmarks;
private long today;
private long day;
@Override
public void setUp()
{
super.setUp();
habit = fixtures.createLongHabit();
checkmarks = habit.getCheckmarks();
checkmarks.getToday(); // compute checkmarks
today = DateUtils.getStartOfToday();
day = DateUtils.millisecondsInOneDay;
}
@Test
public void testAdd()
{
checkmarks.invalidateNewerThan(0);
List<Checkmark> list = new LinkedList<>();
list.add(new Checkmark(0, 0));
list.add(new Checkmark(1, 1));
list.add(new Checkmark(2, 2));
checkmarks.add(list);
List<CheckmarkRecord> records = getAllRecords();
assertThat(records.size(), equalTo(3));
assertThat(records.get(0).timestamp, equalTo(2L));
}
@Test
public void testGetByInterval()
{
long from = today - 10 * day;
long to = today - 3 * day;
List<Checkmark> list = checkmarks.getByInterval(from, to);
assertThat(list.size(), equalTo(8));
assertThat(list.get(0).getTimestamp(), equalTo(today - 3 * day));
assertThat(list.get(3).getTimestamp(), equalTo(today - 6 * day));
assertThat(list.get(7).getTimestamp(), equalTo(today - 10 * day));
}
@Test
public void testGetByInterval_withLongInterval()
{
long from = today - 200 * day;
long to = today;
List<Checkmark> list = checkmarks.getByInterval(from, to);
assertThat(list.size(), equalTo(201));
}
@Test
public void testInvalidateNewerThan()
{
List<CheckmarkRecord> records = getAllRecords();
assertThat(records.size(), equalTo(121));
checkmarks.invalidateNewerThan(today - 20 * day);
records = getAllRecords();
assertThat(records.size(), equalTo(100));
assertThat(records.get(0).timestamp, equalTo(today - 21 * day));
}
private List<CheckmarkRecord> getAllRecords()
{
return new Select()
.from(CheckmarkRecord.class)
.where("habit = ?", habit.getId())
.orderBy("timestamp desc")
.execute();
}
}

View File

@@ -0,0 +1,177 @@
/*
* 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.models.sqlite;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import com.activeandroid.query.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.models.sqlite.records.*;
import org.junit.*;
import org.junit.rules.*;
import org.junit.runner.*;
import java.util.*;
import static junit.framework.Assert.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.*;
@SuppressWarnings("JavaDoc")
@RunWith(AndroidJUnit4.class)
@MediumTest
public class SQLiteHabitListTest extends BaseAndroidTest
{
@Rule
public ExpectedException exception = ExpectedException.none();
private SQLiteHabitList habitList;
private ModelFactory modelFactory;
@Override
public void setUp()
{
super.setUp();
this.habitList = (SQLiteHabitList) super.habitList;
fixtures.purgeHabits(habitList);
modelFactory = component.getModelFactory();
for (int i = 0; i < 10; i++)
{
Habit h = modelFactory.buildHabit();
h.setName("habit " + i);
h.setId((long) i);
if (i % 2 == 0) h.setArchived(true);
HabitRecord record = new HabitRecord();
record.copyFrom(h);
record.position = i;
record.save(i);
}
}
@Test
public void testAdd_withDuplicate()
{
Habit habit = modelFactory.buildHabit();
habitList.add(habit);
exception.expect(IllegalArgumentException.class);
habitList.add(habit);
}
@Test
public void testAdd_withId()
{
Habit habit = modelFactory.buildHabit();
habit.setName("Hello world with id");
habit.setId(12300L);
habitList.add(habit);
assertThat(habit.getId(), equalTo(12300L));
HabitRecord record = getRecord(12300L);
assertNotNull(record);
assertThat(record.name, equalTo(habit.getName()));
}
@Test
public void testAdd_withoutId()
{
Habit habit = modelFactory.buildHabit();
habit.setName("Hello world");
assertNull(habit.getId());
habitList.add(habit);
assertNotNull(habit.getId());
HabitRecord record = getRecord(habit.getId());
assertNotNull(record);
assertThat(record.name, equalTo(habit.getName()));
}
@Test
public void testSize()
{
assertThat(habitList.size(), equalTo(10));
}
@Test
public void testGetAll_withArchived()
{
List<Habit> habits = habitList.toList();
assertThat(habits.size(), equalTo(10));
assertThat(habits.get(3).getName(), equalTo("habit 3"));
}
@Test
public void testGetById()
{
Habit h1 = habitList.getById(0);
assertNotNull(h1);
assertThat(h1.getName(), equalTo("habit 0"));
Habit h2 = habitList.getById(0);
assertNotNull(h2);
assertThat(h1, equalTo(h2));
}
@Test
public void testGetById_withInvalid()
{
long invalidId = 9183792001L;
Habit h1 = habitList.getById(invalidId);
assertNull(h1);
}
@Test
public void testGetByPosition()
{
Habit h = habitList.getByPosition(5);
assertNotNull(h);
assertThat(h.getName(), equalTo("habit 5"));
}
@Test
public void testIndexOf()
{
Habit h1 = habitList.getByPosition(5);
assertNotNull(h1);
assertThat(habitList.indexOf(h1), equalTo(5));
Habit h2 = modelFactory.buildHabit();
assertThat(habitList.indexOf(h2), equalTo(-1));
h2.setId(1000L);
assertThat(habitList.indexOf(h2), equalTo(-1));
}
private HabitRecord getRecord(long id)
{
return new Select()
.from(HabitRecord.class)
.where("id = ?", id)
.executeSingle();
}
}

View File

@@ -0,0 +1,143 @@
/*
* 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.models.sqlite;
import android.support.annotation.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import com.activeandroid.query.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.models.sqlite.records.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import java.util.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.IsNot.not;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class SQLiteRepetitionListTest extends BaseAndroidTest
{
private Habit habit;
private long today;
private RepetitionList repetitions;
private long day;
@Override
public void setUp()
{
super.setUp();
habit = fixtures.createLongHabit();
repetitions = habit.getRepetitions();
today = DateUtils.getStartOfToday();
day = DateUtils.millisecondsInOneDay;
}
@Test
public void testAdd()
{
RepetitionRecord record = getByTimestamp(today + day);
assertThat(record, is(nullValue()));
Repetition rep = new Repetition(today + day);
habit.getRepetitions().add(rep);
record = getByTimestamp(today + day);
assertThat(record, is(not(nullValue())));
}
@Test
public void testGetByInterval()
{
List<Repetition> reps =
repetitions.getByInterval(today - 10 * day, today);
assertThat(reps.size(), equalTo(8));
assertThat(reps.get(0).getTimestamp(), equalTo(today - 10 * day));
assertThat(reps.get(4).getTimestamp(), equalTo(today - 5 * day));
assertThat(reps.get(5).getTimestamp(), equalTo(today - 3 * day));
}
@Test
public void testGetByTimestamp()
{
Repetition rep = repetitions.getByTimestamp(today);
assertThat(rep, is(not(nullValue())));
assertThat(rep.getTimestamp(), equalTo(today));
rep = repetitions.getByTimestamp(today - 2 * day);
assertThat(rep, is(nullValue()));
}
@Test
public void testGetOldest()
{
Repetition rep = repetitions.getOldest();
assertThat(rep, is(not(nullValue())));
assertThat(rep.getTimestamp(), equalTo(today - 120 * day));
}
@Test
public void testGetOldest_withEmptyHabit()
{
Habit empty = fixtures.createEmptyHabit();
Repetition rep = empty.getRepetitions().getOldest();
assertThat(rep, is(nullValue()));
}
@Test
public void testRemove()
{
RepetitionRecord record = getByTimestamp(today);
assertThat(record, is(not(nullValue())));
Repetition rep = record.toRepetition();
repetitions.remove(rep);
record = getByTimestamp(today);
assertThat(record, is(nullValue()));
}
@Nullable
private RepetitionRecord getByTimestamp(long timestamp)
{
return selectByTimestamp(timestamp).executeSingle();
}
@NonNull
private From selectByTimestamp(long timestamp)
{
return new Select()
.from(RepetitionRecord.class)
.where("habit = ?", habit.getId())
.and("timestamp = ?", timestamp);
}
}

View File

@@ -0,0 +1,136 @@
/*
* 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.models.sqlite;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import com.activeandroid.query.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.models.sqlite.records.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import java.util.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
@SuppressWarnings("JavaDoc")
@RunWith(AndroidJUnit4.class)
@MediumTest
public class SQLiteScoreListTest extends BaseAndroidTest
{
private Habit habit;
private ScoreList scores;
private long today;
private long day;
@Override
public void setUp()
{
super.setUp();
habit = fixtures.createLongHabit();
scores = habit.getScores();
today = DateUtils.getStartOfToday();
day = DateUtils.millisecondsInOneDay;
}
@Test
public void testGetAll()
{
List<Score> list = scores.toList();
assertThat(list.size(), equalTo(121));
assertThat(list.get(0).getTimestamp(), equalTo(today));
assertThat(list.get(10).getTimestamp(), equalTo(today - 10 * day));
}
@Test
public void testInvalidateNewerThan()
{
scores.getTodayValue(); // force recompute
List<ScoreRecord> records = getAllRecords();
assertThat(records.size(), equalTo(121));
scores.invalidateNewerThan(today - 10 * day);
records = getAllRecords();
assertThat(records.size(), equalTo(110));
assertThat(records.get(0).timestamp, equalTo(today - 11 * day));
}
@Test
public void testAdd()
{
new Delete().from(ScoreRecord.class).execute();
List<Score> list = new LinkedList<>();
list.add(new Score(today, 0));
list.add(new Score(today - day, 0));
list.add(new Score(today - 2 * day, 0));
scores.add(list);
List<ScoreRecord> records = getAllRecords();
assertThat(records.size(), equalTo(3));
assertThat(records.get(0).timestamp, equalTo(today));
}
@Test
public void testGetByInterval()
{
long from = today - 10 * day;
long to = today - 3 * day;
List<Score> list = scores.getByInterval(from, to);
assertThat(list.size(), equalTo(8));
assertThat(list.get(0).getTimestamp(), equalTo(today - 3 * day));
assertThat(list.get(3).getTimestamp(), equalTo(today - 6 * day));
assertThat(list.get(7).getTimestamp(), equalTo(today - 10 * day));
}
@Test
public void testGetByInterval_withLongInterval()
{
long from = today - 200 * day;
long to = today;
List<Score> list = scores.getByInterval(from, to);
assertThat(list.size(), equalTo(201));
}
private List<ScoreRecord> getAllRecords()
{
return new Select()
.from(ScoreRecord.class)
.where("habit = ?", habit.getId())
.orderBy("timestamp desc")
.execute();
}
}

View File

@@ -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.pebble;
import android.content.*;
import android.support.annotation.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import com.getpebble.android.kit.*;
import com.getpebble.android.kit.util.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.receivers.*;
import org.json.*;
import org.junit.*;
import org.junit.runner.*;
import static com.getpebble.android.kit.Constants.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class PebbleReceiverTest extends BaseAndroidTest
{
private Habit habit1;
private Habit habit2;
@Override
public void setUp()
{
super.setUp();
fixtures.purgeHabits(habitList);
habit1 = fixtures.createEmptyHabit();
habit1.setName("Exercise");
habit2 = fixtures.createEmptyHabit();
habit2.setName("Meditate");
}
@Test
public void testCount() throws Exception
{
onPebbleReceived((dict) -> {
assertThat(dict.getString(0), equalTo("COUNT"));
assertThat(dict.getInteger(1), equalTo(2L));
});
PebbleDictionary dict = buildCountRequest();
sendFromPebbleToAndroid(dict);
awaitLatch();
}
@Test
public void testFetch() throws Exception
{
onPebbleReceived((dict) -> {
assertThat(dict.getString(0), equalTo("HABIT"));
assertThat(dict.getInteger(1), equalTo(habit2.getId()));
assertThat(dict.getString(2), equalTo(habit2.getName()));
assertThat(dict.getInteger(3), equalTo(0L));
});
PebbleDictionary dict = buildFetchRequest(1);
sendFromPebbleToAndroid(dict);
awaitLatch();
}
// @Test
// public void testToggle() throws Exception
// {
// int v = habit1.getCheckmarks().getTodayValue();
// assertThat(v, equalTo(Checkmark.UNCHECKED));
//
// onPebbleReceived((dict) -> {
// assertThat(dict.getString(0), equalTo("OK"));
// int value = habit1.getCheckmarks().getTodayValue();
// assertThat(value, equalTo(200)); //Checkmark.CHECKED_EXPLICITLY));
// });
//
// PebbleDictionary dict = buildToggleRequest(habit1.getId());
// sendFromPebbleToAndroid(dict);
// awaitLatch();
// }
@NonNull
protected PebbleDictionary buildCountRequest()
{
PebbleDictionary dict = new PebbleDictionary();
dict.addString(0, "COUNT");
return dict;
}
@NonNull
protected PebbleDictionary buildFetchRequest(int position)
{
PebbleDictionary dict = new PebbleDictionary();
dict.addString(0, "FETCH");
dict.addInt32(1, position);
return dict;
}
protected void onPebbleReceived(PebbleProcessor processor)
{
BroadcastReceiver pebbleReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
try
{
String jsonData = intent.getStringExtra(MSG_DATA);
PebbleDictionary dict = PebbleDictionary.fromJson(jsonData);
processor.process(dict);
latch.countDown();
targetContext.unregisterReceiver(this);
}
catch (JSONException e)
{
throw new RuntimeException(e);
}
}
};
IntentFilter filter = new IntentFilter(Constants.INTENT_APP_SEND);
targetContext.registerReceiver(pebbleReceiver, filter);
}
protected void sendFromPebbleToAndroid(PebbleDictionary dict)
{
Intent intent = new Intent(Constants.INTENT_APP_RECEIVE);
intent.putExtra(Constants.APP_UUID, PebbleReceiver.WATCHAPP_UUID);
intent.putExtra(Constants.TRANSACTION_ID, 0);
intent.putExtra(Constants.MSG_DATA, dict.toJsonString());
targetContext.sendBroadcast(intent);
}
private PebbleDictionary buildToggleRequest(long habitId)
{
PebbleDictionary dict = new PebbleDictionary();
dict.addString(0, "TOGGLE");
dict.addInt32(1, (int) habitId);
return dict;
}
interface PebbleProcessor
{
void process(PebbleDictionary dict);
}
}

View 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.tasks;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
import java.util.*;
import static junit.framework.Assert.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.IsNot.not;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ExportCSVTaskTest extends BaseAndroidTest
{
@Before
@Override
public void setUp()
{
super.setUp();
}
@Test
public void testExportCSV() throws Throwable
{
fixtures.purgeHabits(habitList);
fixtures.createShortHabit();
List<Habit> selected = new LinkedList<>();
for (Habit h : habitList) selected.add(h);
taskRunner.execute(
new ExportCSVTask(targetContext,habitList, selected, archiveFilename -> {
assertThat(archiveFilename, is(not(nullValue())));
File f = new File(archiveFilename);
assertTrue(f.exists());
assertTrue(f.canRead());
}));
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.tasks;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
import static junit.framework.Assert.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.IsNot.not;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ExportDBTaskTest extends BaseAndroidTest
{
@Before
public void setUp()
{
super.setUp();
}
@Test
public void testExportCSV() throws Throwable
{
ExportDBTask task = new ExportDBTask(targetContext, filename -> {
assertThat(filename, is(not(nullValue())));
File f = new File(filename);
assertTrue(f.exists());
assertTrue(f.canRead());
});
taskRunner.execute(task);
}
}

View File

@@ -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.widgets;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.widgets.*;
import org.junit.*;
import org.junit.runner.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.isoron.uhabits.models.Checkmark.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class CheckmarkWidgetTest extends BaseViewTest
{
private static final String PATH = "widgets/CheckmarkWidgetView/";
private Habit habit;
private CheckmarkList checkmarks;
private FrameLayout view;
@Override
public void setUp()
{
super.setUp();
habit = fixtures.createShortHabit();
checkmarks = habit.getCheckmarks();
CheckmarkWidget widget = new CheckmarkWidget(targetContext, 0, habit);
view = convertToView(widget, 200, 250);
assertThat(checkmarks.getTodayValue(), equalTo(CHECKED_EXPLICITLY));
}
@Test
public void testClick() throws Exception
{
Button button = (Button) view.findViewById(R.id.button);
assertThat(button, is(not(nullValue())));
// A better test would be to capture the intent, but it doesn't seem
// possible to capture intents sent to BroadcastReceivers.
button.performClick();
sleep(1000);
assertThat(checkmarks.getTodayValue(), equalTo(UNCHECKED));
}
@Test
public void testIsInstalled()
{
assertWidgetProviderIsInstalled(CheckmarkWidgetProvider.class);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "checked.png");
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.widgets;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.widgets.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class FrequencyWidgetTest extends BaseViewTest
{
private static final String PATH = "widgets/FrequencyWidget/";
private Habit habit;
private FrameLayout view;
@Override
public void setUp()
{
super.setUp();
setTheme(R.style.TransparentWidgetTheme);
habit = fixtures.createLongHabit();
FrequencyWidget widget = new FrequencyWidget(targetContext, 0, habit);
view = convertToView(widget, 400, 400);
}
@Test
public void testIsInstalled()
{
assertWidgetProviderIsInstalled(FrequencyWidgetProvider.class);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.widgets;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.widgets.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HistoryWidgetTest extends BaseViewTest
{
private static final String PATH = "widgets/HistoryWidget/";
private Habit habit;
private FrameLayout view;
@Override
public void setUp()
{
super.setUp();
setTheme(R.style.TransparentWidgetTheme);
habit = fixtures.createLongHabit();
HistoryWidget widget = new HistoryWidget(targetContext, 0, habit);
view = convertToView(widget, 400, 400);
}
@Test
public void testIsInstalled()
{
assertWidgetProviderIsInstalled(HistoryWidgetProvider.class);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.widgets;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ScoreWidgetTest extends BaseViewTest
{
private static final String PATH = "widgets/ScoreWidget/";
private Habit habit;
private FrameLayout view;
@Override
public void setUp()
{
super.setUp();
setTheme(R.style.TransparentWidgetTheme);
habit = fixtures.createLongHabit();
ScoreWidget widget = new ScoreWidget(targetContext, 0, habit);
view = convertToView(widget, 400, 400);
}
@Test
public void testIsInstalled()
{
assertWidgetProviderIsInstalled(ScoreWidgetProvider.class);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.widgets;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class StreakWidgetTest extends BaseViewTest
{
private static final String PATH = "widgets/StreakWidget/";
private Habit habit;
private FrameLayout view;
@Override
public void setUp()
{
super.setUp();
setTheme(R.style.TransparentWidgetTheme);
habit = fixtures.createLongHabit();
StreakWidget widget = new StreakWidget(targetContext, 0, habit);
view = convertToView(widget, 400, 400);
}
@Test
public void testIsInstalled()
{
assertWidgetProviderIsInstalled(StreakWidgetProvider.class);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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.widgets.views;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class CheckmarkWidgetViewTest extends BaseViewTest
{
private static final String PATH = "widgets/CheckmarkWidgetView/";
private CheckmarkWidgetView view;
private Habit habit;
@Override
@Before
public void setUp()
{
super.setUp();
setTheme(R.style.TransparentWidgetTheme);
habit = fixtures.createShortHabit();
view = new CheckmarkWidgetView(targetContext);
int color = ColorUtils.getAndroidTestColor(habit.getColor());
int score = habit.getScores().getTodayValue();
float percentage = (float) score / Score.MAX_VALUE;
view.setActiveColor(color);
view.setCheckmarkValue(habit.getCheckmarks().getTodayValue());
view.setPercentage(percentage);
view.setName(habit.getName());
view.refresh();
measureView(view, dpToPixels(100), dpToPixels(200));
}
@Test
public void testRender_checked() throws IOException
{
assertRenders(view, PATH + "checked.png");
}
@Test
public void testRender_implicitlyChecked() throws IOException
{
view.setCheckmarkValue(Checkmark.CHECKED_IMPLICITLY);
view.refresh();
assertRenders(view, PATH + "implicitly_checked.png");
}
@Test
public void testRender_largeSize() throws IOException
{
measureView(view, dpToPixels(300), dpToPixels(300));
assertRenders(view, PATH + "large_size.png");
}
@Test
public void testRender_unchecked() throws IOException
{
view.setCheckmarkValue(Checkmark.UNCHECKED);
view.refresh();
assertRenders(view, PATH + "unchecked.png");
}
}

Some files were not shown because too many files have changed in this diff Show More