Introduction

plees-tracker is a simple sleep tracker for your Android phone.

The latest version is v24.2.2, released on 2024-04-01.

Description

This is meant to be a very simple open source clone of PrimeNap, i.e. just track sleep start/stop times and count the average / day.

Features:

  • It literally does nothing while you sleep, causes no battery drain.

  • Stores past sleeps, counts stat from them.

  • Exports/imports sleep data to/from CSV.

  • Can show past sleeps, can selectively delete individual sleeps.

It looks like this:

Contributing

plees-tracker is free and open source. You can find the source code on GitHub and issues and feature requests can be posted on the issue tracker. If you'd like to contribute, please consider opening a pull request.

License

Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

Installation

Building from source

You can build the code using:

./gradlew build

You can run the tests using:

./gradlew test
./gradlew connectedAndroidTest

Installing a binary

Get it on F-Droid:

Get it on F-Droid

Usage

Main activity

plees-tracker is essentially a stopwatch application. It intentionally requires you to manually track your sleep with explicitly starting and ending a tracked sleep. This has the benefit that it's simple: causing no battery drain, nor any privacy problems.

This activity allows:

  • Seeing the status of the tracking: not yet started, in progress and finished.

  • Dashboard: the number of all tracked sleeps, average of sleep durations (disabled by default) and a daily average (in case you sleep multiple times a day or you sometimes skip a whole day) for a customizable duration (see the preferences activity below).

  • A list of past sleeps for the chosen duration: start/stop time for each sleep, awake time and duration counted from these and a rating you can manually specify after the tracking stopped. The awake time of the latest sleep depends on the current time, so it'll increase if you restart plees-tracker.

  • Swiping a sleep left/right will remove the sleep.

  • Tapping on a sleep allows getting to a dedicated sleep activity for a single sleep.

  • A floating action button at the bottom right corner allows to actually start / stop the tracking.

The menu of this activity allows:

  • Import/export your sleeps to CSV. The start and stop columns are UNIX timestamps in milliseconds. The import is incremental, i.e. it remembers what items are imported and the next time only newer items will be imported.

  • Import/export your sleeps to calendar. The export is incremental, i.e. it remembers what items are exported, and the next time only newer items will be exported.

  • See more stats on your sleeps (see the sleep activity below).

  • Customize settings (see the preferences activity below).

Toggle widget

A widget can be added to your home screen. This allows starting or stopping the tracking with a single tap: i.e. it's the same as opening the app and tapping on the start/stop button.

Quick settings tile

A quick settings tile can be added to your panel. This allows starting or stopping the tracking with a single tap: i.e. it's the same as opening the app and tapping on the start/stop button.

Preferences activity

Theme

This allows manually setting the dark mode for plees-tracker. This is useful on Android versions <= 9, where there is no system-provided dark mode. This works out of the box on newer Android versions.

Backup

Backup settings allow you to automatically back up your sleeps after a tracking stopped. This is useful in case you selected a path which is then implicitly synchronized to some external server, e.g. Nextcloud.

Dashboard

You can also customize the dashboard duration, which limits the sleeps and sleep statistics on the dashboard and graphs to the time period selected in the main activity. The default is to only show the past week.

There is also an option to define your ideal sleep length, which is used for some of the graphs (see Graphs activity below).

The other setting influencing the sleep stats is a sleep start delay. Assuming that one presses start, followed by 8 hours, then stop, in case a sleep delay of 15 minutes is set, the recorded sleep length will be 7:45, not 8:00, by increasing the sleep start timestamp.

The 'Show average of sleeps duration' setting is disabled by default and is useful if you always sleep once a day, but sometimes you forget to track your sleep, still you're interested in the average of your sleeps.

The 'Show average of daily sums' setting is enabled by default and is useful if you always track your sleeps, but you may sleep multiple times a day. This will first count the sum of your sleeps within a day, and count the average of those sums.

The 'Ignore empty days when showing average of daily sums' setting is enabled by default and ignores empty days when counting the average of daily sums, assuming that you probably just forgot to track your sleep(s) on that day. If this is not the case and you in fact sometimes skip an entire day, then disable this setting.

Past sleeps

The past sleeps section allow configuring the contents of the individual sleep cards:

  • awake time is hidden by default

  • the read-only rating is hidden by default on the main activity, the read-write rating is always visible in the sleep activity

The sleep cards are not re-created when changing settings, so you need to restart plees-tracker to see the effect.

Do not disturb when tracking

This option enables automatic activation of DND (Do Not Disturb) mode when you start tracking your sleep. When toggled on for the first time, you will be prompted to grant Plees Tracker permissions to modify DND settings.

Upon ceasing sleep tracking, the DND setting you had enabled prior to initiating the tracking will be restored.

Sleep activity

The sleep activity allows modifying the start, stop time or rating of a single recorded sleep, which is useful if you want to update the recorded timestamps to better match reality.

You can also take a multi-line plain text note for the sleep there.

Stats activity

The main activity considers all sleeps for the selected duration when counting the sleeps or when calculating the 2 kind of averages for your sleeps. The stats activity provides the same stats for all possible durations, specifically:

  • last week

  • last two weeks

  • last month

  • last year

  • all time

Graphs activity

The graphs activity provides an alternative way to analyze your sleep data. Currently, the following graphs are provided (select the graph via the menu in the upper right):

  • Deficit/surplus: This graph shows the difference between the ideal sleep length (as customized in settings) versus the actual hours slept per day - positive is surplus, negative is deficit - along with a cumulative total.

  • Length: This graph shows the hours slept per day, along with a cumulative moving average.

  • Start time: This graph shows the start time of the first sleep per day (where day is based on the date of the sleep's ending time), along with a cumulative moving average.

  • Rating: This graph shows the user-provided rating of the sleeps per day, along with a cumulative moving average. Note that no rating counts as 0.

  • Variance: This graph shows the statistical variance and standard deviation of your daily sleep lengths. The more similar sleep lengths you get, the lower the variance will be. The variance units are hours squared and the standard deviation units are hours.

The graphs are generated based on sleeps within the selected dashboard duration.

Credits

Icons made by Dave Gandy and Freepik from Flaticon.

Development notes

Coding style

  • Prefer initializing properties inline over spelling them out in a separate init { block.

  • Use annotations (@Foo) only when it makes the code easier to read.

  • Prefer references (Foo) over nullable references (Foo?).

  • Prefer variable names which are not keywords over working the problem around with backticks ('is').

  • Prefer constructs like foo?.let { over non-null assertions (!!).

Complex parts of the app

The app code is quite simple, this is just fancy stopwatch after all. But some parts are nontrivial:

  • Making sure that the timer doesn't stop, by launching a proper background service was hard to figure out.

  • The icon was surprisingly challenging to add, mostly because every SVG editor will just scale your image, but exactly scaling is ignored by Android Studio's SVG import.

  • The recycler view was tricky to set up: most examples are overcomplicated, when really what was needed here is just an adapter and a holder class.

Kotlin

If you are used to Java, then not spelling out type names all over the place is confusing in Kotlin. See https://stackoverflow.com/questions/54851861/how-do-i-activate-type-annotations-hints-in-kotlin-like-depicted on how to let Android Studio show these types for you without polluting the code.

Debugging

You are supposed to go via the Android logging framework, one possible (temporary, local) debug printf:

Log.e(TAG, "debug, myFunc: myVar is " + myVar)

This way it stands out from the stock debug messages when you filter for package:mine in the Logcat tab in Android Studio.

Changelog

24.2.2

  • Resolves: gh#455 main activity: fix missing update after changing the rating of a sleep
  • add 30/45/60minutes to "Start time delay" option (k3dar)
  • Czech localization added (k3dar)

24.2.1

  • Resolves: gh#419 add optional do not disturb mode integration (Mason Fisher)

24.2.0

  • sleep activity: handle the 'compact view' setting
  • switched version scheme to CalVer to match reality

7.6.5

  • Fix no action on pressing the back button on the action bar
  • Added Themed / Monochrome Launcher icon support for devices that support it (Sébastien Delord)

7.6.4

  • Target Android 14

7.6.3

  • Resolves: gh#407 fix NetworkOnMainThreadException while importing data from a file, backed by cloud storage

7.6.2

  • Resolves: gh#388 add variance and standard deviation graphs (Lastaapps)

7.6.1

  • Resolves: gh#406 export to file: avoid reporting success in toast on failure

7.6.0

  • Resolves: gh#366 ignore empty days when counting average of daily sums

7.5.5

  • Resolves: gh#387 dark mode now also uses different colors for the start/stop button

7.5.4

  • Resolves: gh#373 enable editing start and end date+time of a sleep separately (phiiil)

7.5.3

  • Resolves: gh#358 graphs: add a stop time graph

7.5.2

  • Resolves: gh#365 don't toggle tracking state on rotation (quick settings tile)

7.5.1

  • Resolves: gh#359 fix the widget to start/stop tracking to work on Android 12+

7.5.0

  • Resolves: gh#308 main activity: default to showing average of daily sums only

7.4.5

  • Resolves: gh#292 fix lack of toggle from the notification drawer when app is already foreground

7.4.4

  • Resolves: gh#330 main activity: add placeholder text when there are no sleeps

7.4.3

  • Resolves: gh#275 sleep item layout: add dedicated icon for the 'awake for' row
  • Fixed the tracking notification to work with Android 13

7.4.2

  • Resolves: gh#309 dashboard: hide seconds & timezone by default on the dashboard

7.4.1

  • Resolves: gh#282 lower baseline to API 23 (Android 6.0), covering about 97.4% of devices

7.4.0

  • Resolves: gh#274 add a start time offset
  • Resolves: gh#298 release builds are now signed even outside F-Droid

7.3.5

  • delete all sleep now confirms before erasing starts

7.3.4

  • Add menu item to delete all sleeps (Ely M)
  • Resolves: gh#285 main service: fix crash on Android 12

7.3.3

  • Resolves: gh#277 sleep activity: allow multi-line notes

7.3.2

  • Show timezone at sleep start/stop timestamps
  • Runtime baseline is now API 24 (Android 7.0), still covering more than 80% of devices

7.3.1

  • Resolves: gh#253 fixed sleep activity to allow sleep edits again (Mael Lacour)

7.3.0

  • Resolves: gh#239 it is now possible to comment sleeps
  • No longer using android:onClick, which is broken on older versions of Android

7.2.5

  • Resolves: gh#208 add separators between sleep items in the main activity
  • Resolves: gh#209 improved duplicate filtering in the calendar import/export (Jesper Lillqvist)

7.2.4

  • Resolves: gh#207 avoid duplicate entries when importing from calendar multiple times

7.2.3

  • Resolves: gh#150 avoid duplicate entries when exporting to calendar multiple times

7.2.2

  • Resolves: gh#178 prevent negative sleep durations

7.2.1

  • Resolves: gh#184 fixed overlapped and blocked texts on main page (yuhuitech)

7.2.0

  • Resolves: gh#161 main activity: make the rating widget read-only

7.1.5

  • Resolves: gh#157 hint that swiping the sleep will delete it
  • Resolves: gh#162 avoid empty screen when scrolling down in the stats activity (usashiki)

7.1.4

  • Resolves: gh#138 sleep edit / delete: handle automatic backup

7.1.3

  • Resolves: gh#94 Add quick settings tile to start/stop tracking

7.1.2

  • Awake-for and rating property of the sleep card is now hidden by default

7.1.1

  • Resolves: gh#51 add a graphs menu item and activity to chart various stats over time (usashiki)
  • Resolves: gh#92 add a widget to start/stop tracking with a single stap from the home screen
  • Display time awake after a sleep on the main screen (Sebastian Zeller)
  • Resolves: gh#88 Add dashboard duration setting (usashiki)

7.1.0

  • Resolves gh#90 disable auto-backup bool setting when the user refuses to pick an auto-backup folder

7.0.5

  • Avoid vibration when the sleep notification is created
  • Resolves: gh#32 ability to automatically backup to a storage folder, to be used with e.g. Nextcloud

7.0.4

  • Much faster mass-import of sleeps from a previous export result

7.0.3

  • Resolves: gh#41 ability to export events to your calendar (Ed George)

7.0.2

  • Added PT-BR translation (fabianski7)
  • Tested on Android 11
  • Removed not needed custom fonts, now using default regular/bold fonts from the system
  • Resolves: gh#33 main activity: don't delete entry by swiping on the rating bar
  • Resolves: gh#29 ability to import events from your calendar (Ed George)

7.0.1

  • Fix missing localization of the notification channel's name
  • Updated appcompat, constraintlayout, material, junit and espress-core to latest versions

7.0.0

  • Resolves: gh#28 it is now possible to rate sleeps
  • Resolves: gh#7 expand/collapse FAB on scroll

6.4

  • Resolves: gh#27 improve main activity FAB color in dark mode
  • Added Spannish translation (Diego Sanguinetti)
  • Resolves: gh#6 next to all-time stats, there are now "last 7 days" and "this year" stats as well
  • Related: gh#1 Cannot import csv after export, improved fix for less mainstream Android flavors (Sebastian Zeller)

6.3

  • Resolves: gh#21 daily average now detects completely skipped days
  • Resolves: gh#20 sleep entries are now being sorted in chronological order (Sebastian Zeller)
  • Resolves: gh#19 in the sleep edit time picker, use 24 or 12 hour view according to system settings (Sebastian Zeller)
  • Resolves: gh#16 support dark mode (martiandolphin)

6.2

  • Resolves: gh#15 export format is now better documented
  • Resolves: gh#14 main view: sleep counter is now less confusing for multiple sleeps / day
  • Resolves: gh#13 main view: scroll the content above the recycler view
  • Resolves: gh#8 main view: the snackbar and the start/stop button doesn't overlap anymore

6.1

  • Resolves: gh#11 use different colors for start and stop
  • Much improved design (Sanju S)
  • Resolves: gh#5 Main view: average of daily sum of sleeps is now visible

6.0

  • Main view: the sleep list now has a scrollbar
  • Sleep view: now shows the ID and has a back button

5.0

  • Resolves: gh#2 Allow the user to manually edit an entry

4.0

  • App metadata now features a screenshot
  • Added an about dialog to credit used libraries
  • Now never performing database operations on the main thread
  • Resolves: gh#1 Cannot import csv after export

3.0

  • Can remember already started (but not yet stopped) sleeps on system restart
  • Can show duration of each past sleep
  • Can delete past recorded sleeps selectively

2.0

  • Can import previously exported data
  • Notification icon is now in sync with the launcher icon
  • Runtime baseline is now only API 22 (Android 5.1), not API 26 to cover about 80% of devices

1.0

  • Initial release
  • Can store past sleeps
  • Can count average duration of them

Contributors

Here is a list of the contributors who have helped improving plees-tracker. Big shout-out to them!

If you feel you're missing from this list, feel free to add yourself in a PR.