Skip to content

Replace SharedPreferences with typed data stores - #702

Merged
thestinger merged 7 commits into
GrapheneOS:compose-prototypefrom
m4pl:prefs-settings-repo
Sep 8, 2026
Merged

Replace SharedPreferences with typed data stores#702
thestinger merged 7 commits into
GrapheneOS:compose-prototypefrom
m4pl:prefs-settings-repo

Conversation

@m4pl

@m4pl m4pl commented Sep 3, 2026

Copy link
Copy Markdown

Closes #686

import app.grapheneos.camera.data.core.store.legacyCommonPreferences
import app.grapheneos.camera.data.core.store.removeLegacyCommonKeys

// The stores migrate the shared file in arbitrary order, so each removes only its own keys.

@inthewaves inthewaves Sep 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

By migrating to data store (stored in /data/user/<userid>/app.grapheneos.camera/datastore/), the SharedPreferences exclusion line in https://github.com/m4pl/Camera/blob/prefs-settings-repo/app/src/main/res/xml/full_backup_content.xml no longer applies. We need to confirm if we should continue excluding preferences

Comment on lines 556 to 560
fun updateLastCapturedItem(item: CapturedItem) {
commonPref.edit {
saveLastCapturedItem(item, this)
}

if (mActivity is SecureMainActivity) {
// previous call updated ephemeral SharedPreferences that won't be accessible by the
// "regular" MainActivity
mActivity.applicationContext.getSharedPreferences(
COMMON_SHARED_PREFS_NAME,
Context.MODE_PRIVATE
).edit {
saveLastCapturedItem(item, this)
}
}
runBlocking { capturedItemRepository.saveLastCapturedItem(item) }

lastCapturedItem = item
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

updateLastCapturedItem can now throw java.io.IOException from capturedItemRepository.saveLastCapturedItem, e.g. full disk errors

Comment on lines +324 to +326
settings = runBlocking {
settingsRepository.update { it.copy(gridType = value) }
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Feels like this pattern could maybe block main thread, same for videoQuality and flashMode

@m4pl m4pl Sep 8, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It does block. Will be fixed right after Decompose CamConfig, when the screen moves to a ViewModel.

@sdsantos sdsantos left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All appears to be working fine, and the migration tests I did went smoothly.

if (mActivity !is SecureActivity) {
CapturedItems.init(mActivity, this)
fetchLastCapturedItemFromSharedPrefs()
preferencesScope.launch(Dispatchers.Main.immediate) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why do you need to specify again the Dispatchers.Main.immediate context in launch if it's already set in the CoroutineScope constructor?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

You're right, dropped.

@m4pl
m4pl force-pushed the prefs-settings-repo branch from 5c40e83 to 6899184 Compare September 8, 2026 18:02
@m4pl
m4pl requested a review from inthewaves September 8, 2026 18:04
@m4pl
m4pl force-pushed the prefs-settings-repo branch from 6899184 to 686a415 Compare September 8, 2026 18:20
@m4pl
m4pl changed the base branch from main to compose-prototype September 8, 2026 19:02
@m4pl
m4pl marked this pull request as draft September 8, 2026 19:20
@m4pl
m4pl force-pushed the prefs-settings-repo branch from 686a415 to 2e4bc5d Compare September 8, 2026 20:50
@m4pl
m4pl force-pushed the prefs-settings-repo branch from 2e4bc5d to ee50578 Compare September 8, 2026 21:14
RankoR and others added 7 commits September 8, 2026 23:17
SharedPreferences kept every setting, every SAF grant and the last captured
item in untyped files, read and written on whichever thread asked. Replace it
with three DataStores serialized as JSON - one for what the owner configured,
one for the SAF trees, one for what the app has captured - each opened once in
a SingletonComponent module and reached only through its repository. A
DataMigration per family carries the legacy keys over on first use.

An absent field means "unset", so a value equal to today's default is never
written and a later change to that default still reaches existing installs.
Installs migrated from SharedPreferences are the exception for the per-mode
settings, whose defaults the old code wrote out explicitly.

Lockscreen isolation now falls out of the wiring rather than a branch inside a
store: an ActivityComponent provider hands a SecureActivity an in-memory
snapshot of the owner's settings and SAF grants, while captured-media state
stays durable so a photo taken from the lockscreen survives the session.

Writes now block until the store has committed, where SharedPreferences.apply()
returned before the write landed.
@m4pl
m4pl force-pushed the prefs-settings-repo branch from ee50578 to 9365390 Compare September 8, 2026 21:22
@m4pl
m4pl marked this pull request as ready for review September 8, 2026 21:27

@inthewaves inthewaves left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good, I don't think there's not much else we can do if IOExceptions occur for those calls

@thestinger
thestinger merged commit fe936f6 into GrapheneOS:compose-prototype Sep 8, 2026
2 checks passed
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.

Move preferences to SettingsRepository

5 participants