Repository navigation
Releases: jorgejiro/constanza-android
Release list
Constanza 1.1.0
A complete redesign: calmer, more legible, and the habit list finally tells you when each habit is due.
Install
Open this page on the phone, tap constanza-1.1.0.apk, install over the previous version. It replaces 1.0.0 in place; your habits and history are kept.
What's new
- Graphite look. A neutral dark theme with the Geist typeface and higher text contrast replaces the warm brown one.
- Colour as a dot, not as text. Each habit keeps its colour as a small dot; names are all in one readable tone.
- Clearer answers on Today. Round ✕ and ✓ buttons replace the green and red Yes/No pills. A bar under the date shows how many of today's habits are answered ("2 de 5"), and habits with several reminders show one segment per reminder plus the next time.
- Habit list with schedules. Each habit shows its frequency and reminder time ("Diaria · 09:30", "Lun, mié, vie · 19:00").
- Softer palette. 12 muted colours replace the 21 bright ones. On first launch the database migrates (v7 → v8) and every habit using one of the old preset colours moves to the closest new one; custom colours are kept as they are. Backups exported from earlier versions get the same conversion when imported.
- Editor, Progress, Settings and onboarding restyled to match. The reminder notification's accent is now neutral.
- Fixed: the Today screen used an Android 14-only date constant that could crash on Android 12 and 13.
Permissions are unchanged: the same six entries as 1.0.0, read from this APK with aapt2 dump permissions.
What is NOT verified
Everything was checked on emulators (Android 12 and Android 17), not on a physical phone. The colour migration was tested by the database migration tests, not on a real 1.0.0 install with your data.
Verification
Emulator matrix (:app:emulatorMatrixGroupDebugAndroidTest): Android 12 (API 31) and Android 17 (API 37), 220 instrumented tests each, 0 failed (3 and 6 skipped), including the end-to-end core flow and the v7 → v8 migration. 373 unit tests, lint and detekt green (./gradlew check). Signed with the same certificate as 1.0.0 (CN=Constanza, SHA-256 59659af6…64c912d0); aapt2 reports versionCode 19, versionName 1.1.0.
Constanza 1.0.0
One permission fewer, and the first version number that says "this is the real app".
Install
Open this page on the phone, tap constanza-1.0.0.apk, install over the previous version. It replaces 0.1.16 in place; your habits and history are kept.
What's new
Nothing changes on screen. The app no longer declares ACCESS_NETWORK_STATE.
Constanza never declared it on purpose. It came in with WorkManager, the Android library that runs the reminder work in the background: the library declares it so apps can say "only run this job when there is a network". Constanza never asks for that — nothing in it depends on the network, and it has no internet permission at all — so the entry only sat in the permission list with nothing to justify it. An F-Droid reviewer spotted it; it is now removed explicitly in the manifest.
The two other permissions WorkManager adds, WAKE_LOCK and FOREGROUND_SERVICE, stay. They grant access to none of your data, and removing them is not proven safe. The privacy policy now lists them.
The final permission list is SCHEDULE_EXACT_ALARM, POST_NOTIFICATIONS, RECEIVE_BOOT_COMPLETED, WAKE_LOCK and FOREGROUND_SERVICE, read from this APK with aapt2 dump permissions.
What is NOT verified
Reminders were checked on emulators (Android 12 and Android 17), not on a physical phone.
Verification
Emulator matrix (:app:emulatorMatrixGroupDebugAndroidTest): Android 12 (API 31) and Android 17 (API 37), 206 instrumented tests each, 0 failed (3 and 6 skipped), including the end-to-end core flow: permission dialog, add a habit, reminder notification arrives, answer it, remove the habit. Unit tests and detekt green.
v0.1.16 — a column that promised something it never did
Nothing changes on screen. This one is entirely underneath, and it is worth one honest paragraph.
Install
Open this page on the phone, tap constanza-0.1.16.apk, install over the previous version. The database is rebuilt during this upgrade — see below. Nothing is lost, and a snapshot of the previous state is written before anything is touched.
A field that advertised a feature that does not exist
Every habit carried a sortOrder number, and the query that read the habit list sorted by it.
Nothing ever wrote a value into it. Not once, in any version. The editor set it to zero when you created a habit, and no screen in the app has ever offered a way to reorder anything. So every habit held the same zero, the sort had nothing to distinguish them by, and the database handed rows back in whatever order they happened to be stored.
0.1.15 fixed the visible half of that by sorting the habits screen by name. This release removes the cause: a column that described a manual ordering the app never had. A field like that is worse than clutter — the next person to read the code, months from now, reasonably concludes there is an ordering feature somewhere, and goes looking for it.
Why the database has to be rebuilt
The version of SQLite that ships with Android 12 — the oldest version this app supports — cannot remove a column from a table. That capability arrived in a later release. So removing the field means creating a new habits table, copying every row across, dropping the old one and renaming.
That is delicate here, because habits is the parent of four tables that are set to delete their rows automatically when their habit disappears: your schedules, your reminder times, every answer you have ever recorded, and the pending reminders. Dropping the old table at the wrong moment would take all four with it.
The upgrade counts the rows in all four tables before the rebuild and again after, and refuses to complete if a single one went missing — leaving the database untouched and rolled back rather than quietly emptied. That guard is not new; it was written the last time this table was rebuilt, and it is reused here. It was tested on a real Android 12 device image against a database seeded with habits, schedules, reminders and recorded answers.
Backup files
A backup exported by this version can no longer be read by an older build of the app, because the field it expects is gone. Rather than letting that surface as an unhelpful "invalid file", the format carries a version number that an older build now recognises and reports honestly.
Backups you exported before this release still import normally. The change only affects the other direction.
v0.1.15 — the habit list is alphabetical
The habits screen no longer lists your habits in the order you happened to create them.
Install
Open this page on the phone, tap constanza-0.1.15.apk, install over the previous version. No database change.
The list was never really sorted
The habits screen looked like it was ordered by creation date. It was not ordered by anything.
Every habit carries a sortOrder field, and the query that reads the list sorts by it. But nothing has ever written a value into that field: the editor sets it to 0 on creation and there is no gesture anywhere in the app to reorder a habit. So every row held the same 0, the sort had nothing to distinguish them by, and the database handed them back in the order they were physically stored — which happens to be the order they were added.
It looked like a rule. It was an accident that had been holding steady.
Now the screen sorts by name, and it sorts properly: afeitarme sits between Ánimo and Cenar, where a reader expects it, rather than being exiled for starting with a lowercase letter or carrying an accent. Comparing text by its raw character codes puts every capital ahead of every lowercase letter and throws accented words past the end of the alphabet — so Cenar would have come before afeitarme, and Ánimo after Trabajar. The list now uses a real language-aware comparison instead.
The archived list is sorted the same way.
What did not change
The Today screen decides its own order, and it still does. It groups your habits into sections by time of day, which is a more useful arrangement than the alphabet when the question is what do I do next — so the sorting was applied to the management screen alone, and deliberately not to the query the two screens share.
v0.1.14 — the nightly review actually arrives
The nightly review that 0.1.12 promised never actually arrived. Now it does.
Install
Open this page on the phone, tap constanza-0.1.14.apk, install over the previous version. No database change.
The review notification was never arriving
0.1.12 added a look at the day before the day closes. It never reached you once.
The job that posts it did one thing too early: before reading anything, it booked tomorrow's run. Booking tomorrow's run cancels whatever is currently running under that same booking — and what was currently running was itself. So every night it scheduled its successor, then stopped dead before it could read a single habit, and tomorrow's booking sat there looking perfectly healthy. A notification that was never written, on a schedule that never broke.
That ordering is now the other way round: read the day, post, and only then book tomorrow.
And it could still have been lost, for a second reason
Fixing that was not enough to trust it.
The review was being run by Android's background job scheduler, which is not allowed to wake a sleeping phone at a particular minute — it waits for the phone to wake up anyway. Left overnight on a bedside table, that wait can run past midnight, and past midnight the review deliberately says nothing: the day has closed, the unanswered habits are already marked, and reporting them then would be wrong.
Silence was the correct behaviour. The lateness was the bug.
So the review now uses a real alarm, the same mechanism your habit reminders already use — the one that is allowed to wake the phone, and that this project measured surviving Android's deep sleep. And it moves from 23:30 to 23:00, so there is an hour of room before the day closes instead of thirty minutes.
If you ever revoke the app's exact-alarm permission, it degrades to a ten-minute window rather than failing — and the "say nothing after midnight" rule stays, because a late review is still better silent than wrong.
Reminders now say "Buenos hábitos"
The habit reminder's title read just "Hábitos", which named the subject rather than the point. The app on your home screen is still Constanza; only the notification title changed.
What this release does NOT verify
Whether it arrives on your phone, on a real night. Everything here was proven on emulators, where midnight is a clock a test moves by hand. This is the same sentence 0.1.12 shipped with — and that time it was hiding a real bug, which is exactly why it is worth repeating rather than quietly dropping.
A green matrix still says nothing about One UI throttling or background-kill. That check has been open since the first release and needs a real phone left alone for several days; no emulator can answer it.
So: over the next few nights, the thing to watch is simply whether the notification shows up at 23:00.
Verification
App unit tests 341/341, domain 57/57, detekt clean on main sources.
Instrumented: API 31 (205 tests) and API 37 (205 tests), zero failures on either.
The fix for the cancellation was proven by breaking it again: with the old ordering restored, the new test fails and the five pre-existing tests still pass. Those five could never have caught it — they ran the job without ever booking it under its real name, which is precisely the condition that triggers the bug.
APK signed with the release key and verified with apksigner (CN=Constanza, OU=jjrapps, v2 scheme), aapt2 confirming versionCode 15, versionName 0.1.14.
v0.1.13 — the brightness slider actually does something
The brightness slider actually does something now
Picking a custom habit colour, the third slider was dead across most of its travel: you could only tell it apart at the very bottom, where it went grey, and at the very top. Everything in between produced the same colour.
The slider was never the problem. Every colour it produced was pushed into the app's legibility band — the range of contrast that keeps a habit's name readable on the dark background — and that push rebuilds an out-of-range colour from scratch at the edge of the band, ignoring the brightness you had set. So every position below the edge collapsed onto one colour.
Measured across the slider's 101 positions:
| before | after | |
|---|---|---|
| saturated red | 12 distinct colours | 101 |
| saturated blue | 13 | 95 |
| yellow | 31 | 84 |
| saturated green | 22 | 46 |
Now the slider moves through the band itself: one end is the darkest readable version of your colour, the other the lightest, and every position in between is a different colour. Nothing gets corrected after the fact — the colour is picked inside the readable range to begin with.
Hue and saturation were never affected and are unchanged. Existing habit colours are unchanged; there is no database migration in this release.
Verified before publishing
- 341 app unit tests, 57 domain tests, 0 failures
- 206 instrumented tests on an Android 12 (API 31) emulator, 206 on Android 17 (API 37), 0 failures
- Static analysis clean
- APK signed and version-checked:
versionCode 14,versionName 0.1.13
v0.1.12 — a last look at the day
A last look at the day, before the day decides for you.
Install
Open this page on the phone, tap constanza-0.1.12.apk, install over the previous version. No database change.
A nightly review
At 23:30 the app now sends one notification so you can look at the day before it closes. Tap it and you land on Today, where you can still change anything.
The time is not arbitrary. When a day ends, every habit you never answered is counted as not done — that rule has been there since the beginning and it is the right one. This notification exists to stand in the half hour before it, so what you simply forgot to mark does not get counted as something you did not do.
It tells you what it found. Not a generic nudge every night, but the actual state: three still unanswered, or all answered. So you know whether it is even worth opening before you open it.
It stays quiet when there is nothing to say. If no habit was scheduled at all that day, no notification — otherwise a week of weekday-only habits would mean a message every Saturday and Sunday telling you there was nothing to do.
And it will not lie to you if it arrives late. Should the notification ever be delayed past midnight, it says nothing at all. By then the day has closed and the unanswered habits are already marked; telling you "three still unanswered" at that point would be wrong, and a wrong notification is worse than none.
Two settings, in Settings
- The time. Anything up to 23:45 — the review has to land before the day closes, and the screen says so rather than quietly correcting you.
- When it fires. Every night, which is the default and reads as a closing ritual; or only on nights where something is still unanswered, if you would rather it only spoke when it had something to say.
It has its own notification channel, so you can silence the nightly review from Android's settings without silencing your habit reminders.
What this release does NOT verify
It has been tested on emulators, where "23:30" is a clock the test moves by hand. It has not yet lived through a real night on a real phone, with real Doze and real battery optimisation deciding when a background job is allowed to wake up. That is the thing to watch over the next few days: whether it actually arrives, and on time.
A green matrix also says nothing about One UI throttling or background-kill.
Verification
App unit tests 333/333, domain 57/57, detekt clean on main sources.
Instrumented: API 31 (206 tests) and API 37 (206 tests), zero failures on either, zero dead test classes. Nine new instrumented tests cover the worker, the notification and the settings section, including the one that matters most — a review that fires late posts nothing and still schedules its successor.
Run per device rather than as one job: the combined run was killed for memory, which is a limit of the machine it ran on, not a result.
APK signed with the release key and verified with apksigner (CN=Constanza, OU=jjrapps), aapt2 confirming versionCode 13, versionName 0.1.12, and Auto Backup still off.
v0.1.11 — a loop closing around a tick
A new icon, and a crash nobody on a modern phone could have hit.
Install
Open this page on the phone, tap constanza-0.1.11.apk, install over the previous version. No database change.
The icon
The old one was a spiral drawn in the ocre accent. Two releases ago that accent was deleted from the whole app, which left the icon as the last place it survived — the shape was fine, it had simply been orphaned. That is why it read as wrong without it being obvious why.
The new mark is a clockwise arrow closing a loop around a tick: the task, and the habit of doing it again.
The tick is the same green the Today screen already uses to mark a habit done, so the icon shows the app's own glyph rather than a colour chosen to look nice. The ring stays white because colour, everywhere else in this app, belongs to your habits — and the launcher is the one place the app is allowed an identity of its own without competing with them.
It is a real vector, drawn on Android's adaptive-icon canvas, with a monochrome layer so it also works as a themed icon on Android 13 and above. It was checked in an actual app drawer at 108, 72 and 48dp before shipping.
Choosing a language no longer crashes the app — on phones you do not own
Picking any language other than the system one killed the app on launch, and kept killing it until its data was cleared.
This never affected your phone. The mechanism only runs below Android 13; from 13 onwards the platform applies the language override itself and the app's own code stands aside. Every device this app is developed and tested on is Android 13 or newer, which is exactly why it shipped unseen — the supported devices that do run that code are ones nobody looks at.
The cause was a context handed to the UI that had been detached from the Activity behind it, so anything that needed to find that Activity could no longer get there. It is now wrapped rather than replaced, which keeps both the Activity reachable and the translated strings working — the first attempt at the fix kept one and lost the other, and the test suite caught it.
Settings sections look like sections
The three headings on the Settings screen were plain text in the same style as the options beneath them, so nothing read as a heading. They now use the same treatment the Today screen already had.
Export and Import also stop being quieter than the heading that introduces them — an inversion the accent removal had caused, and one that disappeared on its own once the heading stopped competing with them.
What this release does NOT verify
The icon was seen in an emulator's launcher, not on your home screen among your own apps and wallpaper. That is where it finally has to hold up.
A green matrix also says nothing about One UI throttling, background-kill or real doze.
Verification
App unit tests 304/304, domain 57/57, detekt clean on main sources.
Instrumented: the full matrix — API 31 (197 tests) and API 37 (197 tests), zero failures on either, and zero dead test classes.
The locale fix carries a regression test that was checked in both directions: it passes with the fix and fails with the old code restored. A regression test that does not fail on the bug protects nothing.
APK signed with the release key and verified with apksigner (CN=Constanza, OU=jjrapps), aapt2 confirming versionCode 12, versionName 0.1.11, and Auto Backup still off.
v0.1.10 — your habits stay on your phone
Your habits stay on your phone.
Install
Open this page on the phone, tap constanza-0.1.10.apk, install over the previous version. No database change — installing over 0.1.9 keeps everything you have.
Android was copying your habits to Google Drive
Found the way these things are usually found: uninstalling the app to install a new build, and watching every habit reappear afterwards.
Uninstalling really does delete /data/data. What put the data back was Android Auto Backup — android:allowBackup was true, so the platform had been copying the habit database to the account's Google Drive and restoring it on the next install. The attribute was written into the manifest, but true is also Android's default and nothing explained the choice, so it was template rather than a decision. Nobody picked it.
It is picked now, the other way. Keeping your habits across a reinstall or a new phone is what Export and Import in Settings are for: a file you ask for, keep where you like, and can open and read. A restore you never asked for is how uninstalling quietly stopped meaning "start clean".
Turning off the manifest flag only closes half of it. On Android 12 and above, phone-to-phone transfer is configured separately and would still have carried the database to a new device, so that path is excluded too.
What this means for you, practically
Reinstalling is now a real reset. If you ever want your habits back after wiping the app, export first — nothing will bring them back on its own any more.
Not verified: whether the copy already sitting in the Google Drive of a device that ran an earlier version is deleted, and when. From this version the app neither uploads new data nor restores from any copy, but what Google does with the one already uploaded was not checked here.
Verification
App unit tests 304/304, domain 57/57, detekt clean on main sources.
The backup flag is checked three independent ways rather than one, on the release build itself:
aapt2reading the APK's own manifest reportsallowBackup=falseand the extraction rules present- an instrumented test reads the flag off the merged manifest as the platform parses it — not off the source file, because a library can reintroduce that attribute without anyone touching ours — and passes on a device
adb shell dumpsys packagelists the installed app's flags as[ DEBUGGABLE HAS_CODE ALLOW_CLEAR_USER_DATA ], withALLOW_BACKUPabsent. That is Android's own view, independent of both the source and the test.
That test also exists because the setting comes back by itself: true is the default, so deleting the line reads as tidying rather than as a change of behaviour.
APK signed with the release key and verified with apksigner (CN=Constanza, OU=jjrapps), aapt2 confirming versionCode 11, versionName 0.1.10.
v0.1.9 — one habit, one line
One habit, one line. And an answer you can take back.
Install
Open this page on the phone, tap constanza-0.1.9.apk, install over the previous version.
This one changes the database. Room migration 5 → 6 remaps any habit still holding the retired blue-grey to cyan. Nothing else moves: your habits, schedules, entries and reminders are untouched, and the migration is data-only — 6.json's identityHash is byte-identical to 5.json's, meaning no column, table or index changes. A pre-migration snapshot is written before anything is touched.
The row costs one line instead of three
A habit used to take its name, then a second, nearly empty line holding a small status glyph hard left and a "Cambiar" button hard right, with a void between them. A glyph sitting below and left of the name it describes says nothing about that name.
Now the name takes the width and everything else sits hard right on the name's own line. Nine habits fit where six did. The better gain was unplanned: with every glyph right-aligned, the right edge becomes a column of outcomes, so the whole day reads in one vertical sweep without reading a single name.
The status word is gone — a tick for done, a cross for not done, a dash for skipped. "Pendiente" is gone too: a row offering Sí/No is pending by definition. So is "Omitir", because not answering already amounts to skipping and it did not earn permanent space on every row. Skipped still exists, still renders, and is still reachable when changing an answer.
Sí and No are outlined pills in the same green and red as the glyphs, so the button previews its own result: the green one produces the green tick. Outlined rather than filled, because a filled pill out-weighs the habit names — and the whole point of the previous release was to stop the interface competing with them.
The scheduled time now shows only on habits with several reminders a day, where it is the one thing telling two rows apart. On a habit with one reminder it was the reminder time, not the time anything happened.
"Gestionar hábitos" becomes "Hábitos", and Settings becomes a gear.
Changing an answer, including taking it back
"Cambiar" is gone. Tap an answered habit and pick: Sí, No, Omitido, or Sin responder — back to never having answered at all.
That last one had to respect the rule that a slot left unanswered still counts as a No when its day ends, and it walks straight into that rule's blind spot. Answering does three things at once: it writes the entry, closes the reminder occurrence and cancels the alarm. The midnight sweep only ever looks at open occurrences. So simply deleting the entry would have left the occurrence closed, the sweep would never have seen that habit again, and it would have sat pending for ever — the rule broken inside the feature meant to honour it.
Clearing therefore deletes the entry and reopens the occurrence together, and reopens it to the state a delivered notification leaves behind rather than to "armed". Reopened as armed, the hourly reconcile would have read it as a lost alarm and reposted the notification within the hour, for a moment already gone. No alarm is touched: correcting a mistake must not ring a reminder.
"Sin responder" is offered only on today. On a past day the rule has already decided, so unanswered and "No hecho" are one outcome under two names — offering both would mean asking for one thing and watching a red cross appear instead.
One colour retired
Blue-grey is gone, 22 presets to 21, for the reason silver went last release: painted on a habit's name, a near-neutral stops reading as an identity and starts reading as a disabled row.
What this release does NOT verify
It has been seen, but on an emulator, not on your screen. Whether the row's spacing and the pills read well on a real OLED at arm's length is the thing no test answers.
A green matrix also says nothing about One UI throttling, background-kill or real doze.
Verification
App unit tests 304/304, domain 57/57, detekt clean on main sources.
Instrumented: the full matrix run from scratch — API 31 (190 tests) and API 37 (190 tests), zero failures on either, and zero dead test classes. The end-of-day rule is proved by a test that answers, clears, then runs the real midnight sweep the next day and asserts the habit ends as not done; a second one runs the hourly reconcile an hour after clearing and asserts no alarm was ever touched.
APK signed with the release key and verified with apksigner (CN=Constanza, OU=jjrapps), aapt2 confirming versionCode 10, versionName 0.1.9.