Skip to content

Feat/download notifications (#62 #902 #345)#913

Open
eddyizm wants to merge 16 commits into
developmentfrom
feat/download-notifications
Open

Feat/download notifications (#62 #902 #345)#913
eddyizm wants to merge 16 commits into
developmentfrom
feat/download-notifications

Conversation

@eddyizm

@eddyizm eddyizm commented Jul 19, 2026

Copy link
Copy Markdown
Owner

resolves #62 with a major update of the legacy download notification feature.
partially addresses #902 which was discussed in #345 giving the option to pause, resume and cancel
also partially resolves #755 although it does not give a full new dialog/page fragment. We will put some more features, in a phase 2 after this gets rolled into production. Per a previous discussion, notification improvement could possibly handle all of it. We will see once we get more feedback and usage.

image

Summary by CodeRabbit

  • New Features

    • Added pause, resume, and cancel controls to download notifications.
    • Improved download progress, completion, paused-state, speed, and item-count displays.
    • Added support for exporting completed downloads to a selected device folder.
    • Improved download naming and metadata handling, including external downloads.
  • Bug Fixes

    • Improved download state tracking and handling of queued, active, completed, and cancelled items.
    • Fixed removal and cache synchronization when deleting downloads.
  • Tests

    • Added coverage for download storage, notification names, filename handling, and export behavior.

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fc5b16d1-00d7-4a1a-8f1a-14be0a1b2754

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Download notifications now expose pause, resume, and cancel controls, track progress and completion state, and display download metadata. Repository and cache changes support URI persistence and notification filenames, while completed Media3 downloads are exported to configured external directories.

Changes

Download notification controls and lifecycle

Layer / File(s) Summary
Notification controls and lifecycle
app/src/main/AndroidManifest.xml, app/src/main/java/.../broadcast/receiver/DownloadControlReceiver.java, app/src/main/java/.../service/DownloaderService.java, app/src/main/java/.../util/Constants.kt, app/src/main/res/values/strings.xml
Adds non-exported pause, resume, and cancel broadcast actions, handles those actions through Media3, and builds foreground and paused notifications with progress, speed, completion, and track information.

Download orchestration and metadata lookup

Layer / File(s) Summary
Download orchestration and metadata lookup
app/src/main/java/.../database/dao/DownloadDao.java, app/src/main/java/.../repository/DownloadRepository.java, app/src/main/java/.../service/DownloaderManager.java, app/src/test/java/.../model/*, app/src/test/java/.../repository/*, app/src/test/java/.../service/*
Adds download URI persistence and repository lookup APIs, introduces cached download metadata, updates resume and removal handling, and derives notification filenames from download metadata and URIs.

External export and cache behavior

Layer / File(s) Summary
External export and cache behavior
app/src/main/java/.../util/ExternalAudioWriter.java, app/src/main/java/.../util/ExternalAudioReader.java, app/src/main/java/.../util/ExternalDownloadMetadataStore.java, app/src/main/java/.../ui/fragment/bottomsheetdialog/*, app/src/test/java/.../util/ExternalAudioWriterFilenameTest.java
Replaces size metadata with export-target metadata, delegates download creation to the tracker, exports completed offline downloads to external storage, and simplifies external audio cache updates and filename matching.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DownloaderService
  participant DownloadControlReceiver
  participant Media3DownloadService
  participant ExternalAudioWriter
  DownloaderService->>DownloadControlReceiver: publish pause, resume, or cancel action
  DownloadControlReceiver->>Media3DownloadService: issue download control command
  DownloaderService->>DownloaderService: update notification progress and completion state
  DownloaderService->>ExternalAudioWriter: export completed download by media id
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes add song details to download notifications and browsing metadata, matching #62's request.
Out of Scope Changes check ✅ Passed The modified files all support download notifications, metadata, or tests; no clear unrelated changes stand out.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: download notification enhancements with pause/resume/cancel controls and song info.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/download-notifications

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
app/src/main/java/com/cappielloantonio/tempo/service/DownloaderService.java (1)

373-377: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the declared notification-id constants instead of raw literals. DownloaderService declares FOREGROUND_NOTIFICATION_ID=1, PAUSED_NOTIFICATION_ID=2, COMPLETE_NOTIFICATION_ID=3, but several call sites hardcode the numbers. They match today, so there's no live bug, but any future id change silently desyncs cancel/notify calls.

  • app/src/main/java/com/cappielloantonio/tempo/service/DownloaderService.java#L373-L377: replace cancel(2)/notify(2) with PAUSED_NOTIFICATION_ID and cancel(1) with FOREGROUND_NOTIFICATION_ID.
  • app/src/main/java/com/cappielloantonio/tempo/service/DownloaderService.java#L187: replace manager.notify(3, …) with COMPLETE_NOTIFICATION_ID.
  • app/src/main/java/com/cappielloantonio/tempo/broadcast/receiver/DownloadControlReceiver.java#L50: replace cancel(2) with the shared paused-notification id constant (expose it from DownloaderService).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/cappielloantonio/tempo/service/DownloaderService.java`
around lines 373 - 377, Replace hardcoded notification IDs with the declared
constants: in DownloaderService lines 373-377, use PAUSED_NOTIFICATION_ID for
cancel/notify calls and FOREGROUND_NOTIFICATION_ID for cancel(1); in
DownloaderService line 187, use COMPLETE_NOTIFICATION_ID for manager.notify(3,
…); in DownloadControlReceiver line 50, use the shared PAUSED_NOTIFICATION_ID
exposed from DownloaderService.
app/src/main/java/com/cappielloantonio/tempo/repository/DownloadRepository.java (1)

64-85: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

getDownloadById duplicates getDownload; the Javadoc rationale is inaccurate.

Both methods construct GetDownloadThreadSafedownloadDao.getOne(id), and getOne is SELECT * FROM download WHERE id = :id with no state filter. So getDownloadById is byte-for-byte equivalent to getDownload, and the comment claiming getDownload is "subject to the pending/completed filter used elsewhere" does not match the DAO. Consider delegating to avoid drift and fix the doc.

♻️ Delegate to remove duplication
     public Download getDownloadById(String id) {
-        Download download = null;
-
-        GetDownloadThreadSafe getDownloadThreadSafe = new GetDownloadThreadSafe(downloadDao, id);
-        Thread thread = new Thread(getDownloadThreadSafe);
-        thread.start();
-
-        try {
-            thread.join();
-            download = getDownloadThreadSafe.getDownload();
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-
-        return download;
+        return getDownload(id);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/main/java/com/cappielloantonio/tempo/repository/DownloadRepository.java`
around lines 64 - 85, Update getDownloadById to delegate to the existing
getDownload implementation instead of duplicating the
GetDownloadThreadSafe/thread handling, and revise its Javadoc to accurately
describe the shared getOne lookup without claiming a state filter.
app/src/main/java/com/cappielloantonio/tempo/service/DownloaderManager.java (1)

121-130: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated, divergent filename sanitization.

This sanitizer's char class includes a backslash ([\\/:*?"<>|]), but ExternalAudioWriter.sanitizeFileName and ExternalAudioReader.sanitizeFileName do not include backslash. The predicted external download_uri/notification name computed here can therefore differ from the name the file is actually written under for titles containing \. Consider reusing the single ExternalAudioWriter.sanitizeFileName (now package-visible) to keep prediction, export, and matching in lockstep.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/cappielloantonio/tempo/service/DownloaderManager.java`
around lines 121 - 130, Update the filename construction in DownloaderManager to
reuse ExternalAudioWriter.sanitizeFileName instead of maintaining the local
replaceAll sanitization logic. Preserve the existing baseName construction and
fallback behavior, and ensure the predicted download URI uses the same sanitized
name as ExternalAudioWriter and ExternalAudioReader.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/com/cappielloantonio/tempo/service/DownloaderManager.java`:
- Around line 233-249: The display-name logic in getDisplayFileName must avoid
deriving “download” from internal MusicUtil URIs. Detect the internal download
path and construct the notification name from the track title and artist, while
retaining the current URI-based filename behavior for external exports and the
existing title/extension fallback.

In `@app/src/main/java/com/cappielloantonio/tempo/service/DownloaderService.java`:
- Around line 184-213: Update the notification construction in
DownloaderService, including the completion, paused, and downloading title/text
paths (and the corresponding occurrence near line 345), to use localized
strings.xml resources instead of hardcoded English. Add a quantity plural
resource for the tracks-downloaded success message and format all dynamic counts
through resource arguments, preserving the existing notification content and
behavior.
- Around line 205-213: Update the active-download title in DownloaderService so
the displayed current index, derived from completed + 1, is capped at
batchTotal. Preserve the paused title and the existing speed suffix and
zero-total behavior.

In `@app/src/main/java/com/cappielloantonio/tempo/util/ExternalAudioWriter.java`:
- Around line 155-180: The export failure path in ExternalAudioWriter must
remove targetFile so streaming errors or early failures cannot leave partial
downloads; apply this in the catch and any early failure path such as the out ==
null branch. In
app/src/main/java/com/cappielloantonio/tempo/util/ExternalAudioWriter.java lines
155-180, update the cleanup around the export task while preserving successful
writes. In
app/src/main/java/com/cappielloantonio/tempo/util/ExternalAudioReader.java lines
185-196, keep the unconditional complete-file behavior synchronized with this
writer cleanup guarantee; no separate size verification is needed.

---

Nitpick comments:
In
`@app/src/main/java/com/cappielloantonio/tempo/repository/DownloadRepository.java`:
- Around line 64-85: Update getDownloadById to delegate to the existing
getDownload implementation instead of duplicating the
GetDownloadThreadSafe/thread handling, and revise its Javadoc to accurately
describe the shared getOne lookup without claiming a state filter.

In `@app/src/main/java/com/cappielloantonio/tempo/service/DownloaderManager.java`:
- Around line 121-130: Update the filename construction in DownloaderManager to
reuse ExternalAudioWriter.sanitizeFileName instead of maintaining the local
replaceAll sanitization logic. Preserve the existing baseName construction and
fallback behavior, and ensure the predicted download URI uses the same sanitized
name as ExternalAudioWriter and ExternalAudioReader.

In `@app/src/main/java/com/cappielloantonio/tempo/service/DownloaderService.java`:
- Around line 373-377: Replace hardcoded notification IDs with the declared
constants: in DownloaderService lines 373-377, use PAUSED_NOTIFICATION_ID for
cancel/notify calls and FOREGROUND_NOTIFICATION_ID for cancel(1); in
DownloaderService line 187, use COMPLETE_NOTIFICATION_ID for manager.notify(3,
…); in DownloadControlReceiver line 50, use the shared PAUSED_NOTIFICATION_ID
exposed from DownloaderService.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 34810407-b26e-4bcc-83b8-1756a572d658

📥 Commits

Reviewing files that changed from the base of the PR and between 6dcd52e and 43c9202.

📒 Files selected for processing (16)
  • app/src/main/AndroidManifest.xml
  • app/src/main/java/com/cappielloantonio/tempo/broadcast/receiver/DownloadControlReceiver.java
  • app/src/main/java/com/cappielloantonio/tempo/database/dao/DownloadDao.java
  • app/src/main/java/com/cappielloantonio/tempo/repository/DownloadRepository.java
  • app/src/main/java/com/cappielloantonio/tempo/service/DownloaderManager.java
  • app/src/main/java/com/cappielloantonio/tempo/service/DownloaderService.java
  • app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/DownloadedBottomSheetDialog.java
  • app/src/main/java/com/cappielloantonio/tempo/util/Constants.kt
  • app/src/main/java/com/cappielloantonio/tempo/util/ExternalAudioReader.java
  • app/src/main/java/com/cappielloantonio/tempo/util/ExternalAudioWriter.java
  • app/src/main/java/com/cappielloantonio/tempo/util/ExternalDownloadMetadataStore.java
  • app/src/main/res/values/strings.xml
  • app/src/test/java/com/cappielloantonio/tempo/model/DownloadModelTest.kt
  • app/src/test/java/com/cappielloantonio/tempo/repository/DownloadRepositoryTest.kt
  • app/src/test/java/com/cappielloantonio/tempo/service/DownloaderManagerNotificationTest.kt
  • app/src/test/java/com/cappielloantonio/tempo/util/ExternalAudioWriterFilenameTest.java

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.

1 participant