Skip to content

Update what's kept on the phone in the background - #408

Merged
hatstand merged 1 commit into
masterfrom
claude/project-thread-wugi27-background-sync
Nov 6, 2026
Merged

hatstand merged 1 commit into
masterfrom
claude/project-thread-wugi27-background-sync

Conversation

@hatstand

@hatstand hatstand commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

The second of two PRs for background sync, after #407. Until now, what's kept on the phone only synced while the app was connected to Clementine. Now it's updated in the background too, so music set to Every time I connect stays up to date without opening the app.

When it runs

BackgroundSync schedules SyncWorker as unique periodic work, every 6 hours. It's scheduled at startup and again whenever the setting or Download over changes, with ExistingPeriodicWorkPolicy.UPDATE so jobs don't stack. Android runs it only when the phone is in a fit state for work that can wait:

  • charging, idle, and with battery and storage not low;
  • on a network Download over allows: UNMETERED for Wi‑Fi only, NOT_ROAMING for Wi‑Fi and mobile data, and CONNECTED for any network. Data Saver keeps background work off the network as well.

In practice that's mostly overnight on home Wi‑Fi.

App is WorkManager's Configuration.Provider, and the manifest removes WorkManager's startup initializer, so WorkManager is set up on first use. That also works in Robolectric tests, which don't run startup initializers.

What a run does

  1. If the app is connected, it leaves the sync to the app's connection (Syncer.syncNow()).
  2. It stops if there's no last Clementine (address, port, code and sync server id), no rule that would sync, or Download over doesn't allow the network.
  3. It opens a BackgroundConnection of its own to the last address. This is a plain ClementineSimpleConnection with no media session, renderer or notification, and it doesn't start ClementineService. It waits for Clementine's INFO and checks that it's the Clementine the rules are for and that it syncs. Another Clementine at the same address is left alone.
  4. It syncs through Syncer.syncInBackground, which shares the app's sync slot. So the progress notification and the Kept on this phone rows work as before. A sync asked for while it runs, such as when the app connects, runs after it.
  5. It disconnects and returns success() whatever happened. Clementine not being on is the usual case, so WorkManager waits for the next period rather than retrying.

WorkManager stops a worker after 10 minutes. SyncEngine takes a new stopped check: once it's true, it downloads nothing more, the rule being synced fails and the rest aren't tried. Everything downloaded so far stays, and the next run carries on.

This doesn't run as a dataSync foreground service, which the plan suggested for long syncs. That would need a Play Console declaration before the dev builds could be uploaded, and each 10-minute run carries on where the last one stopped anyway. It can come later if long syncs turn out to need it.

The setting

"Update kept music in the background", under Download over, on by default. It shows only with the testing features, like the rest of media sync, so there's no release note. The strings have the same English and translator notes as the iOS client's (iOS-Remote#82).

Tests

  • BackgroundSyncTest covers:
    • the constraints for each Download over choice;
    • finding the last Clementine;
    • which rules would sync;
    • checking the Clementine that answers;
    • BackgroundConnection against a fake Clementine: it connects and gets a manifest, a wrong code is refused, and nothing is there.
  • SyncEngineTest.aStoppedSyncDownloadsNothingMoreAndKeepsWhatItHas

🤖 Generated with Claude Code

https://claude.ai/code/session_01R2bAq6kZW6eyymkA3zsd4p

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Store screenshots

From run 42298232038, against clementine-it. Left: the store listing of the latest release, v13.12, which shows the dark theme. Right: this pull request, dark and light.

Screen v13.12 This PR, dark This PR
1_player
2_library
2_library_album
3_playlist
4_search
5_connection
6_connect
10_internet –
11_internet_radio –
fold_half_open_player –
fold_inner_player –
fold_outer_player –
settings –

Base automatically changed from claude/project-thread-wugi27-sync-updated to master November 6, 2026 21:36
A periodic WorkManager job, SyncWorker, syncs the rules for the last
Clementine that synced, at its last address, about every 6 hours. It
runs only when Android sees fit for work that can wait: charging, idle,
battery and storage not low, and on a network Download over allows. It
opens a connection of its own, checks that the Clementine answering is
the one the rules are for, syncs through Syncer, and disconnects. When
the app is already connected, its connection syncs instead.

When Android stops the job, SyncEngine downloads nothing more and keeps
what it has, so the next run carries on. A setting under Download over,
on by default, turns it off. App sets WorkManager up on first use, as
its Configuration.Provider, so it's there in Robolectric tests too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R2bAq6kZW6eyymkA3zsd4p
@hatstand
hatstand force-pushed the claude/project-thread-wugi27-background-sync branch from 602b430 to 62ce939 Compare November 6, 2026 21:37
@hatstand
hatstand enabled auto-merge (rebase) November 6, 2026 21:39
@hatstand
hatstand merged commit 05d9beb into master Nov 6, 2026
8 checks passed
@hatstand
hatstand deleted the claude/project-thread-wugi27-background-sync branch November 6, 2026 21:53
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