Relative date tests (12):
- next week, in N days, next monday/friday parsing
- Cleaned text verification (metadata stripped)
- Future date assertions, exact day-count checks
Tag inheritance tests (3):
- Task-level tag overrides memo tags
- No task tag inherits memo tags
- Empty memo tags = empty lists
Parser doctor tests (4):
- No false positives for next monday, next week, in N days
- Reminder with relative date valid
162 tests passing.
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context)
CalendarReminderManager:
- Creates hidden "Nikki Tasks" calendar via CalendarContract
- Syncs tasks with due dates as calendar events with reminders
- Uses SYNC_DATA1 for task ID deduplication (sync adapter URI)
- Cleans up events for completed/deleted tasks
- Handles SecurityException gracefully if permission denied
Wired into both triggerReminderCheck() and TaskCheckWorker.
AlarmManager kept for p1 tasks that bypass DND.
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context)
Share intent receiver:
- ACTION_SEND text/plain intent filter in manifest
- Shared text pre-fills compose card via MainActivity → App →
AppNavHost → MainScreen → MemoListScreen
Relative dates in task parser:
- "next monday" through "next sunday" (picks next occurrence)
- "next week" (7 days from today)
- "in N days" / "in N day" (arbitrary future offset)
- All cleaned from display text
Task count badge on pivot header:
- Small number next to "tasks" title showing overdue + due-today
- Uses accent color, only visible when count > 0
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context)
Tasks tab:
- Undo snackbar on task toggle with 3s window
- Haptic feedback on checkbox toggle
- Pull-to-refresh triggers memo sync
- Empty state: "all clear" + subtitle
Memos tab:
- Haptic feedback on memo post
- Scroll to top after posting
- Empty state: "nothing here yet" + subtitle
Editor crash fix:
- Robust backspace/enter handling for auto-inserted task lines
- Guard against dropLast on short strings
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context)
Compose card toolbar:
- "add task" button inserts - [ ] on new line
- "due" and "at" buttons appear when editing a task line
- Date/time pickers insert formatted values at cursor
- "+" opens existing insert menu (media, code block)
Per-task live preview:
- Each task shown as a row: checkbox + text + metadata chips
- Replaces flat bag of unassociated chips
- Same preview in both compose card and inline memo editor
Editor improvements:
- Monospace font in both compose and inline editors
- TextFieldValue for cursor control (cursor moves to end)
- Auto-continue: enter after task line inserts new - [ ]
- Backspace on empty auto-inserted line removes it
- Enter on empty task line exits task mode
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context)
1. Remove println debug statement from ReminderScheduler
2. Drop scheduledIds tracking — AlarmManager deduplicates via
FLAG_UPDATE_CURRENT, and the tracking prevented rescheduling
when a task's due time was edited
3. TaskCheckWorker uses live app DB via liveMemosProvider when
available, falls back to opening its own DB only when the
app process isn't running (boot receiver, background check)
4. Default notify time synced from DataStore to SharedPreferences
via syncNotifyTime() — both DirectAlarmScheduler and
TaskCheckWorker now read from the same source via
readDefaultNotifyTime() helper
5. TaskReminderReceiver triggers runTaskCheckNow() after each
alarm fires, so the next alarm is scheduled immediately
instead of waiting up to 15 min for WorkManager
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context)
Build workflow: runs tests + assembles debug APK on pull requests.
Release workflow: builds signed APK and uploads to GitHub release.
Signing config reads keystore from env vars (set by CI secrets).
Keystore files excluded from git.
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context)
- README.md: introduction, features, build/install, tech stack,
disclaimer, acknowledgements, logo at top
- TASK_FORMAT.md: full task syntax reference with examples,
validation rules, typo detection, notification behavior
- Fix archived memos: use state query param instead of CEL filter
- LOGO.md: remove triangle variant, keep circle only
- logo-circle.svg tracked for README rendering
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context)
Memos instances may run on local networks, Tailscale (100.x),
or other private infrastructure without TLS. The app defaults
to HTTPS in the login flow; HTTP requires explicit user input.
Android's domain-config doesn't support IP/CIDR matching, so
cleartext is permitted globally with documentation.
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context)
1. Filter injection: escape quotes/backslashes in search query
before interpolating into API filter parameter
2. Backup data leak: configure backup_rules.xml and
data_extraction_rules.xml to exclude sharedprefs, database,
and datastore files from cloud backup and device transfer
3. Cleartext traffic: add network_security_config.xml with
cleartextTrafficPermitted=false, referenced from manifest
4. Debug logging: remove all Log.d() calls from
TaskCheckWorker, DirectAlarmScheduler, TaskReminderReceiver
that logged task content and scheduling details
5. Token obfuscation: XOR + Base64 obfuscation for credentials
stored in DataStore. Prefixed with "OBF:" for seamless
migration of existing plaintext values on next login.
Not cryptographic — prevents casual file inspection.
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context)
Parser doctor:
- validateContent() with error/warning severity levels
- Errors: invalid date, invalid priority, reminder without due,
invalid reminder unit
- Warnings: time without date, past date, multiple metadata,
typo detection with correction suggestions
- 21 new tests in ParserDoctorTest
Tag inheritance:
- extractTasks() accepts memoTags parameter
- Tasks without #tags inherit memo-level tags
Offline queue:
- PendingSyncEntity + PendingSyncDao for queued edits
- MemoRepository queues failed API calls, drains on next sync
- Sync status banner: "synced N min ago" / "offline · N pending"
UI:
- Parser doctor banner in tasks tab with inline highlighting
- Error/warning dots on task rows
- Pivot headers with absolute positioning and measured widths
- Logo in settings about section (Canvas-drawn circle variant)
Other:
- MIT license
- Logo design spec (LOGO.md)
- Concentric circle logo: pink circle, black+teal 47° annular wedge
- .gitignore updated for dev artifacts
144 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
reliable alarm scheduling
Auto sync:
- Configurable interval: 1/2/5/10/15/30/60 min (default 5)
- MemoRepository.syncIntervalMinutes updated live from DataStore
- Setting shown under "memos" section in settings
Notifications by priority:
- p1: 🔴 alarm sound, long vibration, wakes screen, bypasses DND
- p2: 🟠 notification sound, medium vibration, heads-up
- p3: 🔵 notification sound, short vibration
- none: silent, no vibration
- BigTextStyle with priority tag, colored accent bar
- 4 separate Android notification channels
Background reliability:
- FOREGROUND_SERVICE, WAKE_LOCK, REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
- Battery optimization exemption requested on first launch
- DirectAlarmScheduler: schedules alarms directly from app process
using live Room data (bypasses WorkManager DB sync issue)
- onContentChanged fires direct scheduling on every memo create/update
- TaskReminderReceiver moved to androidApp module for reliable
cold-start instantiation
- WorkManager kept as 15-min backup for server-side changes
123 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Notification fixes:
- Removed scheduledIds skip — alarms always recomputed on each worker
run (AlarmManager deduplicates via FLAG_UPDATE_CURRENT)
- Tasks with dueTime but no dueDate now assume today
- Worker runs once immediately on app launch + periodic 15min
- Added debug logging to trace task/alarm computation
- "check reminders now" button in settings triggers immediate check
Custom reminder picker:
- Number input field (digits only) + unit selector (min/hr/day/week)
- Replaces the preset-only list with free-form input
- "save" validates > 0 before applying, "clear" removes reminder
ReminderScheduler extracted to commonMain (testable):
- 18 tests covering: completed/no-date skip, due time alarms,
default 8am/8pm, duration offsets (min/hr/day/week),
past alarm filtering, multiple tasks, label preservation
125 tests total, all passing.
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Archived memos:
- "view archived memos" in explorer navigates to memo list with
archived filter active
- Fetches archived memos from API (state == ARCHIVED)
- MemoCard shows "restore" instead of "archive" in context menu
- Restore calls updateMemo(state=NORMAL), upserts to local cache
- Compose card hidden when viewing archived
- Filter banner shows "archived memos" with clear option
Comment deletion:
- Each comment shows "delete" link next to creator/date
- Calls deleteMemo on the comment (child memo) and removes locally
Accessibility:
- Task group collapse icons now have contentDescription
("Expand"/"Collapse")
Test suite: 107 tests (5 new entity mapper tests)
- MemoEntityMappersTest: round-trip field preservation, empty tags,
cachedAt, all visibility variants, timestamp fidelity
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Loading state:
- CircularProgressIndicator shown during first fetch instead of
"no memos yet". isInitialLoading flag in MemoListUiState cleared
after first emission from observeMemos().
Error display:
- Failed refresh/load errors shown as red text in memo list
- Previously errors were captured but never displayed to user
Archived memos:
- listArchivedMemos() API endpoint added (filter: state == ARCHIVED)
- "view archived memos" link in explorer page (placeholder for full UI)
102 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
1. Default visibility now used in compose card (reads from DataStore)
2. Week start day wired to explorer calendar (rotates day labels,
adjusts Zeller offset)
3. Task toggle deduplicated: both MemoListViewModel and
MemoDetailViewModel now use TaskParser.toggleTaskInContent
instead of manual string replacement
4. reactToMemo refetches single memo instead of all memos
5. AppDependencies stores Job reference, cancels before re-init
6. HTTP timeouts added: 30s request/socket, 15s connect
102 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Task detail sheet:
- "open in memo" and "close" aligned on same line (left/right)
- Due date+time, reminder, priority, tags as labeled rows with
bordered value boxes (accent when set, muted when not)
- No presets for date/time — tap opens DatePicker/TimePicker
- Reminder picker: duration options (15min to 1week)
- Tag editor: text field for #tag input
- Dialog stays open after selections, "close" to dismiss
Reminder as duration (!Nunit):
- !30min, !1hr, !2day, !1week parsed into ReminderDuration
- Notification worker: alarm at dueTime - duration offset
- Shown in task list metadata row
Live preview in compose card:
- Shows parsed metadata chips as you type
- Auto-checklist: Enter after task line inserts "- [ ] "
Settings additions:
- Default visibility (tap cycles: private → protected → public)
- Default reminder (tap cycles: none → 15min → 30min → 1hr → 1day)
- Week starts on (tap cycles through days, for calendar)
- All new settings persisted in DataStore
Parser rewritten:
- Removed @labels entirely (@ not a Memos concept)
- Natural dates without prefix: today, tomorrow, yesterday
- Word boundary checks: mp3 ≠ p3, todaying ≠ today
- ISO date colons don't match as time
- !reminder not parsed as due time
102 tests (64 parser, 16 mapper, 7 serialization, 7 backup,
5 visibility, 3 apiResult), all passing.
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Time parsing:
- 12-hour format: 5pm, 2:30pm, 11am, 12am (midnight), 12pm (noon)
- 24-hour format: 14:30, 9:00, 17:00
- Time extracted alongside date: "@today 3pm p1 #work"
- Time cleaned from display text
- dueTime field added to Task domain model (LocalTime?)
Notification scheduling:
- Tasks with specific time: AlarmManager.setExactAndAllowWhileIdle at
that exact time
- Tasks with date only: two alarms at 8am and 8pm on the due date
- SecurityException fallback to inexact alarm if permission denied
- TaskAlarmReceiver fires notification when alarm triggers
- BootReceiver re-schedules WorkManager on device reboot
Permissions added:
- SCHEDULE_EXACT_ALARM, USE_EXACT_ALARM for AlarmManager
- RECEIVE_BOOT_COMPLETED for reschedule after reboot
Test suite: 76 tests (38 TaskParser, 16 DtoMappers, 7 Serialization,
7 Backup, 5 Visibility, 3 ApiResult), all passing.
9 new time parsing tests: 12h am/pm, 12h with minutes, 24h, midnight,
noon, no time returns null, time+date combo, time cleaned from text.
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Refresh now does a full sync:
- Fetches ALL pages from the API (loops until nextPageToken is empty)
- Clears the entire local cache before inserting fresh data
- Deleted/archived memos on server are now properly removed locally
AMOLED theme dialog fix:
- surface raised from #000000 to #161616 (dialogs no longer invisible)
- surfaceContainer at #1E1E1E for popup/dialog backgrounds
- surfaceContainerHigh at #222222 for elevated surfaces
- outline brightened to #444444 for better border visibility
- All AlertDialog containerColor changed to surfaceContainer
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Media upload:
- Android file picker via ActivityResultContracts.GetContent
- Upload attachment API (base64 content to /api/v1/attachments)
- Uploaded attachments linked to memo via CreateMemoRequest.attachments
- Upload status shown in compose card ("uploading...", "N attachment(s) ready")
Markdown improvements:
- Clickable links: URLs and [text](url) open in browser via LinkAnnotation
- Table rendering: pipe-delimited markdown tables with header row
- Fixed underscore italic (_text_) and bold (__text__) variants
Comments:
- Comments section at bottom of MemoDetailScreen
- List existing comments via /api/v1/memos/{id}/comments
- Add new comments with text input + send button
- Comments rendered with full markdown
Share:
- "share" option in memo long-press context menu
- Android share intent with memo content as plain text
Also:
- Task toggle now works in memo feed (onTaskToggle callback wired)
- Tag clicks in explorer filter memos page
- Search from explorer filters memos page instead of navigating
- All three filter types (date/tag/search) shown as banner with "clear"
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Tasks screen:
- Group by: Due Date, List/Topic, Priority, Source Memo, Status
- Sort by: Due Date, Priority (within groups)
- Collapsible groups with count badges
- Accent-colored checkboxes with content-based task identification
- Task detail bottom sheet: edit due date, priority, open source memo
- Completed tasks in collapsed group at bottom
Settings screen:
- Metro-style large section headers (24sp Light)
- Accent color picker: grid of 20 WP8 color circles
- Theme toggle: dark / light / amoled
- Account info display, sign-out with confirmation
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
MainScreen: WP Panorama-style navigation with parallax-scrolling pivot
headers (explore/memos/tasks/settings). Headers drift at 50% of swipe
speed. Active title in accent color, inactive fades with distance.
MemoListScreen: cardless feed with inline compose bar (expandable text
field, + insert menu, visibility picker, accent "post" button).
Pull-to-refresh, date-based filtering from explorer calendar.
MemoDetailScreen: full content view with markdown rendering, attachment
display, reactions. Back link, retry on load failure.
MemoEditorScreen: Metro-style editor with 32sp Light title, accent
underline text field, dirty state tracking, discard confirmation dialog.
ExplorerPage: activity calendar (month heatmap with navigation arrows),
search bar, tag list with counts. Calendar dates link to memos page
with date filter instead of opening detail screen.
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Color system:
- Dark (#1F1F1F bg), Light (white bg), AMOLED (pure black bg)
- All 20 WP8 accent colors (Lime through Taupe)
- User-selectable accent color persisted in DataStore
- Default: Cobalt (#0050EF)
Typography (WP scale):
- 54sp Light for page titles, 32sp Light for pivot headers
- 24sp Light for section headers, 17sp body, 15sp normal, 14sp small
- SemiBold for emphasis, Light for large display text
Flat surfaces only — no elevation, no tonal surfaces, no Material chrome.
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
- AppDependencies: manual DI container with lazy singletons
- TokenStore: DataStore-backed persistence for server URL, access token,
theme preference, and accent color selection
- DataStoreFactory: multiplatform DataStore creation
- LocalAppDependencies: CompositionLocal for DI access in composables
- App.kt: root composable with theme + Coil ImageLoader (Ktor engine)
- MainActivity: creates AppDependencies, provides via CompositionLocal
- Coil configured with authenticated Ktor HttpClient for private images
Co-Authored-By: Claude Opus 4.6 (1M context)
Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>