Compare commits

...

402 Commits

Author SHA1 Message Date
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
668 changed files with 35952 additions and 15019 deletions

51
.gitignore vendored
View File

@@ -1,36 +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/
*.actual.png
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

View File

@@ -1,6 +1,88 @@
# Changelog
### 1.4.0 (April 4, 2016)
### 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
@@ -62,4 +144,4 @@
### 1.0.0 (February 19, 2016)
* Initial release
* Initial release

143
NOTICE.md
View File

@@ -59,28 +59,6 @@ under the SIL OFL 1.1.
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
### DragSortListView
<https://github.com/bauerca/drag-sort-listview>
A subclass of the Android ListView component that enables drag
and drop re-ordering of list items.
Copyright 2012 Carl Bauer
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.
### Material Design Icons
<https://github.com/google/material-design-icons>
@@ -107,4 +85,123 @@ Extended linear layout that wrap its content when there is no place in the curre
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.
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.

View File

@@ -1,10 +1,13 @@
# Loop Habit Tracker
<a href="https://circleci.com/gh/iSoron/uhabits/tree/dev">
<img src="https://img.shields.io/circleci/project/iSoron/uhabits/dev.svg">
</a>
<!--
<a href="https://codecov.io/github/iSoron/uhabits?branch=dev">
<img src="https://img.shields.io/codecov/c/github/iSoron/uhabits.svg" alt="Coverage via Codecov" />
</a>
-->
Loop is a simple Android app that helps you create and maintain good habits,
allowing you to achieve your long-term goals. Detailed graphs and statistics
@@ -16,6 +19,15 @@ source.
<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
[![Main screen][screen1th]][screen1]
[![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
@@ -47,20 +59,12 @@ source.
and there will never be. The complete source code is available under the
GPLv3.
## Screenshots
[![Main screen][screen1th]][screen1]
[![Edit habit][screen2th]][screen2]
[![Habit strength][screen3th]][screen3]
[![Habit history and streaks][screen4th]][screen4]
[![Widgets][screen5th]][screen5]
## 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 [building instructions][build].
app from the source code, see [build instructions][build].
## Contributing
@@ -71,7 +75,8 @@ 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.
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
@@ -87,32 +92,39 @@ contribute, even if you are not a software developer.
## License
This program 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.
<img align="right" src="https://www.gnu.org/graphics/gplv3-88x31.png">
This program 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.
Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
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,19 +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 "23.0.1"
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "org.isoron.uhabits"
minSdkVersion 15
targetSdkVersion 23
targetSdkVersion 25
buildConfigField "Integer", "databaseVersion", "13"
buildConfigField "Integer", "databaseVersion", "15"
buildConfigField "String", "databaseFilename", "\"uhabits.db\""
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//testInstrumentationRunnerArgument "size", "small"
testInstrumentationRunnerArgument "size", "medium"
}
buildTypes {
@@ -29,30 +32,111 @@ android {
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 'org.apmem.tools:layouts:1.10@aar'
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 project(':libs:drag-sort-listview:library')
compile files('libs/ActiveAndroid.jar')
compile 'org.apmem.tools:layouts:1.10@aar'
compile 'org.jetbrains:annotations-java5:15.0'
androidTestCompile 'com.android.support:support-annotations:23.1.1'
androidTestCompile 'com.android.support.test:runner:0.4.1'
androidTestCompile 'com.android.support.test:rules:0.4.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'
}
provided 'javax.annotation:jsr250-api:1.0'
testApt 'com.google.dagger:dagger-compiler:2.2'
task grantAnimationPermission(type: Exec, dependsOn: 'installDebug') {
commandLine "adb shell pm grant org.isoron.uhabits android.permission.SET_ANIMATION_SCALE".split(' ')
}
testCompile 'junit:junit:4.12'
testCompile 'org.hamcrest:hamcrest-library:1.3'
testCompile 'org.mockito:mockito-core:1.10.19'
tasks.whenTaskAdded { task ->
if (task.name.startsWith('connected')) {
task.dependsOn grantAnimationPermission
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

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

@@ -1,66 +0,0 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits;
import android.content.Context;
import android.os.Build;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
import org.isoron.uhabits.helpers.DateHelper;
import org.isoron.uhabits.tasks.BaseTask;
import org.junit.Before;
import java.util.concurrent.TimeoutException;
public class BaseTest
{
protected Context testContext;
protected Context targetContext;
private static boolean isLooperPrepared;
public static final long FIXED_LOCAL_TIME = 1422172800000L; // 8:00am, January 25th, 2015 (UTC)
@Before
public void setup()
{
if(!isLooperPrepared)
{
Looper.prepare();
isLooperPrepared = true;
}
targetContext = InstrumentationRegistry.getTargetContext();
testContext = InstrumentationRegistry.getContext();
DateHelper.setFixedLocalTime(FIXED_LOCAL_TIME);
}
protected void waitForAsyncTasks() throws InterruptedException, TimeoutException
{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
{
Thread.sleep(1000);
return;
}
BaseTask.waitForTasks(10000);
}
}

View File

@@ -17,47 +17,46 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.unit.views;
package org.isoron.uhabits;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.SystemClock;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.graphics.*;
import android.os.*;
import android.support.annotation.*;
import android.view.*;
import android.widget.*;
import org.isoron.uhabits.BaseTest;
import org.isoron.uhabits.helpers.DatabaseHelper;
import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.tasks.BaseTask;
import org.isoron.uhabits.views.HabitDataView;
import org.isoron.uhabits.utils.*;
import org.isoron.uhabits.widgets.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import static junit.framework.Assert.fail;
import static android.view.View.MeasureSpec.*;
import static junit.framework.Assert.*;
public class ViewTest extends BaseTest
public class BaseViewTest extends BaseAndroidTest
{
protected static final double SIMILARITY_CUTOFF = 0.09;
protected static final double DEFAULT_SIMILARITY_CUTOFF = 0.09;
public static final int HISTOGRAM_BIN_SIZE = 8;
protected void measureView(int width, int height, View view)
{
int specWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
int specHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
private double similarityCutoff;
view.measure(specWidth, specHeight);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
@Override
public void setUp()
{
super.setUp();
similarityCutoff = DEFAULT_SIMILARITY_CUTOFF;
}
protected void assertRenders(View view, String expectedImagePath) throws IOException
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();
@@ -65,38 +64,140 @@ public class ViewTest extends BaseTest
int width = actual.getWidth();
int height = actual.getHeight();
Bitmap scaledExpected = Bitmap.createScaledBitmap(expected, width, height, true);
Bitmap scaledExpected =
Bitmap.createScaledBitmap(expected, width, height, true);
double distance;
boolean similarEnough = true;
if ((distance = compareHistograms(getHistogram(actual), getHistogram(scaledExpected))) > SIMILARITY_CUTOFF)
if ((distance = compareHistograms(getHistogram(actual),
getHistogram(scaledExpected))) > similarityCutoff)
{
similarEnough = false;
errorMessage.append(String.format(
"Rendered image has wrong histogram (distance=%f). ",
distance));
"Rendered image has wrong histogram (distance=%f). ",
distance));
}
if(!similarEnough)
if (!similarEnough)
{
saveBitmap(expectedImagePath, ".scaledExpected", scaledExpected);
String path = saveBitmap(expectedImagePath, ".actual", actual);
errorMessage.append(String.format("Actual rendered image " + "saved to %s", path));
saveBitmap(expectedImagePath, ".expected", scaledExpected);
String path = saveBitmap(expectedImagePath, "", actual);
errorMessage.append(
String.format("Actual rendered image saved to %s", path));
fail(errorMessage.toString());
}
actual.recycle();
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;
@@ -115,112 +216,31 @@ public class ViewTest extends BaseTest
}
}
if(result == null)
result = "views/" + path;
if (result == null) result = "views/" + path;
return result;
}
private String saveBitmap(String filename, String suffix, Bitmap bitmap)
throws IOException
throws IOException
{
File dir = DatabaseHelper.getSDCardDir("test-screenshots");
if(dir == null) dir = DatabaseHelper.getFilesDir("test-screenshots");
if(dir == null) throw new RuntimeException("Could not find suitable dir for screenshots");
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);
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()));
if (!parent.exists() && !parent.mkdirs()) throw new RuntimeException(
String.format("Could not create dir: %s",
parent.getAbsolutePath()));
FileOutputStream out = new FileOutputStream(absolutePath);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
return absolutePath;
}
private int[][] getHistogram(Bitmap bitmap)
{
int histogram[][] = new int[4][256 / HISTOGRAM_BIN_SIZE];
for(int x = 0; x < bitmap.getWidth(); x++)
{
for(int y = 0; y < bitmap.getHeight(); y++)
{
int color = bitmap.getPixel(x, y);
int[] argb = new int[]{
(color >> 24) & 0xff, //alpha
(color >> 16) & 0xff, //red
(color >> 8) & 0xff, //green
(color ) & 0xff //blue
};
histogram[0][argb[0] / HISTOGRAM_BIN_SIZE]++;
histogram[1][argb[1] / HISTOGRAM_BIN_SIZE]++;
histogram[2][argb[2] / HISTOGRAM_BIN_SIZE]++;
histogram[3][argb[3] / HISTOGRAM_BIN_SIZE]++;
}
}
return histogram;
}
private double compareHistograms(int[][] actualHistogram, int[][] expectedHistogram)
{
long diff = 0;
long total = 0;
for(int i = 0; i < 256 / HISTOGRAM_BIN_SIZE; i ++)
{
diff += Math.abs(actualHistogram[0][i] - expectedHistogram[0][i]);
diff += Math.abs(actualHistogram[1][i] - expectedHistogram[1][i]);
diff += Math.abs(actualHistogram[2][i] - expectedHistogram[2][i]);
diff += Math.abs(actualHistogram[3][i] - expectedHistogram[3][i]);
total += actualHistogram[0][i];
total += actualHistogram[1][i];
total += actualHistogram[2][i];
total += actualHistogram[3][i];
}
return (double) diff / total / 2;
}
protected int dpToPixels(int dp)
{
return (int) UIHelper.dpToPixels(targetContext, dp);
}
protected void tap(GestureDetector.OnGestureListener view, int x, int y) throws InterruptedException
{
long now = SystemClock.uptimeMillis();
MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, dpToPixels(x),
dpToPixels(y), 0);
view.onSingleTapUp(e);
e.recycle();
}
protected void refreshData(final HabitDataView view)
{
new BaseTask()
{
@Override
protected void doInBackground()
{
view.refreshData();
}
}.execute();
try
{
waitForAsyncTasks();
}
catch (Exception e)
{
throw new RuntimeException("Time out");
}
}
}

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

@@ -17,24 +17,24 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.unit;
package org.isoron.uhabits;
import android.os.Build;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.os.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.HabitsApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.isoron.uhabits.activities.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.IOException;
import java.io.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class HabitsApplicationTest
@MediumTest
public class HabitsApplicationTest extends BaseAndroidTest
{
@Test
public void test_getLogcat() throws IOException
@@ -45,7 +45,8 @@ public class HabitsApplicationTest
String msg = "LOGCAT TEST";
new RuntimeException(msg).printStackTrace();
String log = HabitsApplication.getLogcat();
BaseSystem system = new BaseSystem(targetContext);
String log = system.getLogcat();
assertThat(log, containsString(msg));
}
}

View File

@@ -17,56 +17,44 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.unit.views;
package org.isoron.uhabits.activities.common.views;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.unit.HabitFixtures;
import org.isoron.uhabits.views.HabitFrequencyView;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class HabitFrequencyViewTest extends ViewTest
@MediumTest
public class FrequencyChartTest extends BaseViewTest
{
private HabitFrequencyView view;
public static final String BASE_PATH = "common/FrequencyChart/";
private FrequencyChart view;
@Override
@Before
public void setup()
public void setUp()
{
super.setup();
super.setUp();
HabitFixtures.purgeHabits();
Habit habit = HabitFixtures.createLongHabit();
fixtures.purgeHabits(habitList);
Habit habit = fixtures.createLongHabit();
view = new HabitFrequencyView(targetContext);
view.setHabit(habit);
refreshData(view);
measureView(dpToPixels(300), dpToPixels(100), view);
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, "HabitFrequencyView/render.png");
}
@Test
public void testRender_withTransparentBackground() throws Throwable
{
view.setIsBackgroundTransparent(true);
assertRenders(view, "HabitFrequencyView/renderTransparent.png");
}
@Test
public void testRender_withDifferentSize() throws Throwable
{
measureView(dpToPixels(200), dpToPixels(200), view);
assertRenders(view, "HabitFrequencyView/renderDifferentSize.png");
assertRenders(view, BASE_PATH + "render.png");
}
@Test
@@ -75,6 +63,20 @@ public class HabitFrequencyViewTest extends ViewTest
view.onScroll(null, null, -dpToPixels(150), 0);
view.invalidate();
assertRenders(view, "HabitFrequencyView/renderDataOffset.png");
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

@@ -17,60 +17,50 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.unit.views;
package org.isoron.uhabits.activities.common.views;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import android.util.*;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.unit.HabitFixtures;
import org.isoron.uhabits.views.HabitScoreView;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class HabitScoreViewTest extends ViewTest
@MediumTest
public class ScoreChartTest extends BaseViewTest
{
private static final String BASE_PATH = "common/ScoreChart/";
private Habit habit;
private HabitScoreView view;
private ScoreChart view;
@Override
@Before
public void setup()
public void setUp()
{
super.setup();
super.setUp();
HabitFixtures.purgeHabits();
habit = HabitFixtures.createLongHabit();
fixtures.purgeHabits(habitList);
habit = fixtures.createLongHabit();
view = new HabitScoreView(targetContext);
view.setHabit(habit);
view = new ScoreChart(targetContext);
view.setScores(habit.getScores().toList());
view.setColor(ColorUtils.getColor(targetContext, habit.getColor()));
view.setBucketSize(7);
refreshData(view);
measureView(dpToPixels(300), dpToPixels(100), view);
measureView(view, dpToPixels(300), dpToPixels(200));
}
@Test
public void testRender() throws Throwable
{
Log.d("HabitScoreViewTest", String.format("height=%d", dpToPixels(100)));
assertRenders(view, "HabitScoreView/render.png");
}
@Test
public void testRender_withTransparentBackground() throws Throwable
{
view.setIsBackgroundTransparent(true);
assertRenders(view, "HabitScoreView/renderTransparent.png");
}
@Test
public void testRender_withDifferentSize() throws Throwable
{
measureView(dpToPixels(200), dpToPixels(200), view);
assertRenders(view, "HabitScoreView/renderDifferentSize.png");
Log.d("HabitScoreViewTest",
String.format("height=%d", dpToPixels(100)));
assertRenders(view, BASE_PATH + "render.png");
}
@Test
@@ -79,26 +69,40 @@ public class HabitScoreViewTest extends ViewTest
view.onScroll(null, null, -dpToPixels(150), 0);
view.invalidate();
assertRenders(view, "HabitScoreView/renderDataOffset.png");
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.refreshData();
view.invalidate();
assertRenders(view, "HabitScoreView/renderMonthly.png");
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.refreshData();
view.invalidate();
assertRenders(view, "HabitScoreView/renderYearly.png");
assertRenders(view, BASE_PATH + "renderYearly.png");
}
}

View File

@@ -17,58 +17,57 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.unit.views;
package org.isoron.uhabits.activities.common.views;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.unit.HabitFixtures;
import org.isoron.uhabits.views.HabitStreakView;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class HabitStreakViewTest extends ViewTest
@MediumTest
public class StreakChartTest extends BaseViewTest
{
private HabitStreakView view;
private static final String BASE_PATH = "common/StreakChart/";
private StreakChart view;
@Override
@Before
public void setup()
public void setUp()
{
super.setup();
super.setUp();
HabitFixtures.purgeHabits();
Habit habit = HabitFixtures.createLongHabit();
fixtures.purgeHabits(habitList);
Habit habit = fixtures.createLongHabit();
view = new HabitStreakView(targetContext);
measureView(dpToPixels(300), dpToPixels(100), view);
view.setHabit(habit);
refreshData(view);
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, "HabitStreakView/render.png");
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, "HabitStreakView/renderTransparent.png");
}
@Test
public void testRender_withSmallSize() throws Throwable
{
measureView(dpToPixels(100), dpToPixels(100), view);
refreshData(view);
assertRenders(view, "HabitStreakView/renderSmallSize.png");
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.03f);
latch = new CountDownLatch(1);
view = new CheckmarkButtonView(targetContext);
view.setValue(Checkmark.UNCHECKED);
view.setColor(ColorUtils.getAndroidTestColor(7));
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");
}
}

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