Skip to content

Optional auto-cleanup of recordings older than N days (engine only)#148

Open
dsremo wants to merge 1 commit into
Dimowner:masterfrom
dsremo:dsremo/recordings-retention
Open

Optional auto-cleanup of recordings older than N days (engine only)#148
dsremo wants to merge 1 commit into
Dimowner:masterfrom
dsremo:dsremo/recordings-retention

Conversation

@dsremo

@dsremo dsremo commented May 15, 2026

Copy link
Copy Markdown

What this is

An opt-in retention engine that deletes recording files older than a user-set number of days. Disabled by default (max_age_days = 0); zero behavior change for existing users.

How it works

  • New util/RecordingsRetention object exposes:
    • setMaxAgeDays(context, days) — set the threshold
    • getMaxAgeDays(context) — read it
    • sweepIfDue(context, recordingDir) — delete files older than the threshold, throttled to one run per 24h
  • ARApplication.onCreate calls sweepIfDue() with the active recording directory. When max_age_days = 0 it short-circuits, so the patch is dormant unless the user opts in.
  • Storage: a single SharedPreferences file (recordings_retention) holds max_age_days (Int) and last_run (Long). Kept separate from the app's main prefs so restoring main prefs from a backup doesn't unexpectedly arm this feature.

What is not in this PR

A Settings UI to expose max_age_days. I'm intentionally keeping this PR to the engine — the UX call (a number field in SettingsActivity? A duration picker? A dropdown with presets like 30/90/365 days?) is a maintainer choice. Until a UI lands the feature is dormant (the engine is already wired in ARApplication.onCreate).

If you'd prefer me to bundle a Settings row in this same PR, happy to extend. I left it out because:

  1. Reviewing a single self-contained utility is cheaper than reviewing a Settings flow at the same time.
  2. The UI fits your existing aesthetic better if you design it.

Safety properties

  • Default offmax_age_days = 0 is the initial value; no sweep ever runs unless the user explicitly sets a non-zero value.
  • 24h throttlelast_run timestamp prevents repeat scans on rapid app re-launches or process restarts.
  • runCatching per file — a single file that can't be deleted (FS busy, permission edge-case) doesn't abort sweep on the remaining files.
  • Only regular files — only entries where file.isFile && file.lastModified() < cutoff are deleted. Never the directory itself, never subfolders.
  • No external services — pure on-device, no network, no notifications.

Files

  • app/src/main/java/com/dimowner/audiorecorder/util/RecordingsRetention.kt (new, 57 lines)
  • app/src/main/java/com/dimowner/audiorecorder/ARApplication.kt (+5 lines — the call site)

Tested

  • max_age_days = 0 (default): launching the app shows no log output for the sweep path, no files touched. ✓
  • max_age_days = 30 set via RecordingsRetention.setMaxAgeDays(ctx, 30): launching the app deletes files older than 30 days from the recording dir, leaves newer ones alone. ✓
  • Second launch within 24h: 24h throttle kicks in, no scan. ✓

Why this exists

A long-running user accumulates a lot of voice notes over months; many become irrelevant after a few weeks. Manual cleanup is friction. Other recorder apps in the same niche have offered this for years (Easy Voice Recorder, Smart Voice Recorder); it's a known-good UX. This PR brings the same primitive without forcing a UX choice on you.

@Dimowner

Copy link
Copy Markdown
Owner

I don't know who would need such feature.

@dsremo

dsremo commented Jun 1, 2026

Copy link
Copy Markdown
Author

Generally people who have the habit of keeping records of meetings etc record all type of things for use in the future but after 1 year they become redundant and they have to go and delete one by one its better to have a autocleanup which will keep cleaning old records and this is opt in just a new feature addtion

@Dimowner

Dimowner commented Jun 1, 2026

Copy link
Copy Markdown
Owner

Maybe it is better to add a feature that allows us to delete all records older than a specified date. That would be more clear for users. I don't like the feature idea, which can cause records to disappear from the list unexpectedly.

…rash

Replaces the prior auto-cleanup engine on this branch per maintainer
feedback. The previous version registered a background sweep on
application start and silently removed recordings older than a
configured threshold; @Dimowner objected that records could disappear
without explicit user action.

This rewrite implements the alternative @Dimowner suggested: a manual
action under Settings that lets the user pick a cutoff date and review
what will happen before anything is deleted.

Flow
- Settings → 'Delete old recordings…' opens DatePickerDialog (default
  cutoff: 3 months ago; max date clamped to today).
- After a date is picked, a confirmation dialog quotes the formatted
  date and asks for explicit Yes/No via the project's existing
  AndroidUtils.showDialogYesNo helper.
- On confirmation, SettingsPresenter walks getAllRecords() on the
  recordingsTasks background queue and calls localRepository
  .deleteRecord(id) for each record whose added/created timestamp is
  older than the cutoff. That method routes the recording through the
  app's existing Trash (markAsTrashRecord + trashDataSource), so the
  delete is recoverable from the Trash tab — not a permanent removal.
- On completion the activity shows a toast with the moved count
  (plural-aware) or 'No recordings older than that date were found'
  if zero matched.

Why through Trash rather than deleteRecordForever
- The Trash flow already exists for individual-record deletes from
  the records list; reusing it gives this bulk action the same safety
  net (24h-undo via the Trash tab) without introducing a new code
  path.
- Addresses the 'disappear unexpectedly' concern directly: even after
  confirmation, the user can still recover anything they didn't mean
  to delete.

Scope
- No SharedPreferences, no boot/scheduled work, no background
  observers. The previous PR's RecordingsRetention.kt and its
  ARApplication.kt call site are gone.
- All strings are pluralised where needed (old_records_moved_to_trash
  has one/other forms) and use existing dimens/styles for the
  Settings row so it visually matches Rate/Request.

Files touched (5)
- SettingsContract.java: +1 user action, +2 view callbacks.
- SettingsPresenter.java: deleteRecordsOlderThan implementation
  on the recordingsTasks queue.
- SettingsActivity.java: button wiring, date picker, confirm dialog,
  view callbacks for moved-count and empty-result.
- activity_settings.xml: new 'Delete old recordings…' row above the
  Settings statistics block, ic_delete_forever icon.
- strings.xml: 4 new strings + 1 plurals entry.
@dsremo dsremo force-pushed the dsremo/recordings-retention branch from 3161df4 to a8975ec Compare June 18, 2026 09:23
@dsremo

dsremo commented Jun 18, 2026

Copy link
Copy Markdown
Author

Thanks for the feedback @Dimowner — fully agreed that records disappearing without an explicit user action is the wrong shape. I've force-pushed a rewrite that takes your suggestion directly.

What the PR does now:

  • Removes the auto-sweep engine and the ARApplication boot hook entirely.
  • Adds a single Settings entry "Delete old recordings…" that opens a DatePickerDialog (defaults to 3 months ago, max-date clamped to today).
  • After the user picks a date, a confirmation dialog quotes the formatted date and asks Yes/No via the project's existing AndroidUtils.showDialogYesNo helper.
  • On confirmation, SettingsPresenter walks getAllRecords() on the recordings background queue and calls localRepository.deleteRecord(id) for matches — which routes through the existing Trash path (markAsTrashRecord + trashDataSource), so the delete is recoverable from the Trash tab.
  • Toast on completion: "Moved N records to Trash" (plural-aware) or "No recordings older than that date were found" if zero matched.

Why Trash rather than deleteRecordForever: the Trash flow already exists for individual deletes from the records list; reusing it gives this bulk action the same safety net so users can still recover anything they didn't mean to delete, even after confirming.

Net diff: +110 lines across 5 files (SettingsContract / SettingsPresenter / SettingsActivity / activity_settings.xml / strings.xml). The previous RecordingsRetention.kt and its boot caller are gone. Built locally on JDK 21 + Gradle 8 — clean, only pre-existing WaveformView.java deprecation warnings.

Happy to adjust UX further if you want different defaults, a different default cutoff, or to relocate the action somewhere other than Settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants