Named settings profiles
Split out from RFC #95 by @mikenrafter.
Problem
MakeACopy has one set of settings. Someone who scans receipts in the morning
and book chapters in the evening changes OCR language, aspect ratio, output
format and edge-detection behaviour every time they switch.
Proposal
Let the user save the current settings under a name and switch between saved
profiles with one tap.
- A profile captures the user-facing preferences: camera options, crop aspect
ratio and snapping, OCR language and preprocessing, layout and
post-processing options, PDF and JPEG output options, cleanup, focus and
exposure.
- A profile picker in the options dialog, showing the active profile by name.
- Create, rename, duplicate, delete.
- Export a profile to a file and import one, via the system file picker, so
profiles can be shared between devices.
- Switching profiles first saves the current settings into the profile being
left, then applies the selected one, so no change is lost.
Profiles hold preferences only. They never own scanned pages, the batch queue,
or the document library.
Foundation in main
The settings were restructured before this request so that profiles can be
built without a hand-written list of keys:
de.schliweb.makeacopy.settings.SettingsCatalog lists every user-facing
preference with its file, key, type, default and group. SettingsCatalogTest
scans the sources and fails when a preference is written anywhere without
being listed. A profile is a snapshot of this catalog.
OptionsDialogFragment is the one options dialog (Scan, Camera, OCR, Export,
Library, App). The OCR, Export and Library screens open it at their own group
and see only that group and the ones after it. The options themselves live in
OcrOptionsPanel, ExportOptionsPanel and LibraryOptionsPanel, which read
from and write to SharedPreferences.
Design
- Scope. Add a
scope to each catalog entry: PROFILE or DEVICE. Scan,
Camera, OCR, Export, Crop and Library settings are PROFILE, including the
inbox folder URI, so a "books" profile and a "notes" profile can export to
different folders. App language, accessibility mode and the active profile
id are DEVICE and stay out of profiles. A PROFILE entry can additionally
be marked deviceBound; it is stored in the profile but omitted from the
exported file and ignored on import. The inbox folder URI is the first such
entry: it is a storage permission of this device and means nothing on
another one. inbox_enabled travels with the profile but only takes effect
once a folder is chosen.
- Snapshot.
SettingsSnapshot.capture(context) and
apply(context, snapshot) iterate SettingsCatalog.ALL by type. No key is
named twice; a new catalog entry is in the snapshot automatically.
- File format. Versioned JSON:
{"version": 1, "app": "<versionName>", "settings": {...}}. On import, unknown keys are ignored and missing keys are
filled from the catalog defaults, so older exports stay readable.
- Storage. A Room entity
SettingsProfile(id, name, json, createdAt, updatedAt) with a regular, non-destructive migration. The active profile id
is a DEVICE preference.
- Picker. A row at the top of the options dialog, above the groups:
"Profile: ▾". It is offered only when the dialog is opened from the
camera screen, because switching a profile changes Scan and Camera settings,
which the later screens must not touch for the current document. From the
other screens the row shows the name without a menu, or is hidden.
Selecting a profile loads its values into the dialog's controls (the panels
can read from a SharedPreferences implementation backed by the snapshot),
Confirm writes them and marks the profile active, Cancel changes nothing.
Before applying, the settings being left are saved into the previous
profile.
- Menu. Switch, save as, rename, duplicate, delete, export, import. With
no profiles saved yet, the row offers only "Save as profile…".
- Import/export through the storage access framework, file suffix
.makeacopy-profile.json. Separate PR.
Acceptance criteria
- A fresh install behaves exactly as today. Profiles only appear once the user
creates one.
- Every
PROFILE setting in SettingsCatalog is part of the snapshot; there
is no separate key list to maintain.
- Plain-JVM unit tests cover snapshot round trip (via a
SharedPreferences
fake), JSON round trip, and importing a file of an older version.
- Exported profile files are versioned so older exports can still be imported
after the schema changes.
- Storage in the existing Room database with a proper migration; no
destructive migration.
- The picker is only offered from the camera screen.
- A profile exported on one device and imported on another never carries a
folder URI; after the import, inbox mode waits for a folder to be chosen.
- All new strings are translated before release.
Reference
The fork linked in #95 has an implementation with a Room entity, a snapshot
helper covering 37 preference keys, a bottom-sheet picker and SAF import and
export. It is a reference for scope, not a base for the code.
Named settings profiles
Split out from RFC #95 by @mikenrafter.
Problem
MakeACopy has one set of settings. Someone who scans receipts in the morning
and book chapters in the evening changes OCR language, aspect ratio, output
format and edge-detection behaviour every time they switch.
Proposal
Let the user save the current settings under a name and switch between saved
profiles with one tap.
ratio and snapping, OCR language and preprocessing, layout and
post-processing options, PDF and JPEG output options, cleanup, focus and
exposure.
profiles can be shared between devices.
left, then applies the selected one, so no change is lost.
Profiles hold preferences only. They never own scanned pages, the batch queue,
or the document library.
Foundation in
mainThe settings were restructured before this request so that profiles can be
built without a hand-written list of keys:
de.schliweb.makeacopy.settings.SettingsCataloglists every user-facingpreference with its file, key, type, default and group.
SettingsCatalogTestscans the sources and fails when a preference is written anywhere without
being listed. A profile is a snapshot of this catalog.
OptionsDialogFragmentis the one options dialog (Scan, Camera, OCR, Export,Library, App). The OCR, Export and Library screens open it at their own group
and see only that group and the ones after it. The options themselves live in
OcrOptionsPanel,ExportOptionsPanelandLibraryOptionsPanel, which readfrom and write to
SharedPreferences.Design
scopeto each catalog entry:PROFILEorDEVICE. Scan,Camera, OCR, Export, Crop and Library settings are
PROFILE, including theinbox folder URI, so a "books" profile and a "notes" profile can export to
different folders. App language, accessibility mode and the active profile
id are
DEVICEand stay out of profiles. APROFILEentry can additionallybe marked
deviceBound; it is stored in the profile but omitted from theexported file and ignored on import. The inbox folder URI is the first such
entry: it is a storage permission of this device and means nothing on
another one.
inbox_enabledtravels with the profile but only takes effectonce a folder is chosen.
SettingsSnapshot.capture(context)andapply(context, snapshot)iterateSettingsCatalog.ALLby type. No key isnamed twice; a new catalog entry is in the snapshot automatically.
{"version": 1, "app": "<versionName>", "settings": {...}}. On import, unknown keys are ignored and missing keys arefilled from the catalog defaults, so older exports stay readable.
SettingsProfile(id, name, json, createdAt, updatedAt)with a regular, non-destructive migration. The active profile idis a
DEVICEpreference."Profile: ▾". It is offered only when the dialog is opened from the
camera screen, because switching a profile changes Scan and Camera settings,
which the later screens must not touch for the current document. From the
other screens the row shows the name without a menu, or is hidden.
Selecting a profile loads its values into the dialog's controls (the panels
can read from a
SharedPreferencesimplementation backed by the snapshot),Confirm writes them and marks the profile active, Cancel changes nothing.
Before applying, the settings being left are saved into the previous
profile.
no profiles saved yet, the row offers only "Save as profile…".
.makeacopy-profile.json. Separate PR.Acceptance criteria
creates one.
PROFILEsetting inSettingsCatalogis part of the snapshot; thereis no separate key list to maintain.
SharedPreferencesfake), JSON round trip, and importing a file of an older version.
after the schema changes.
destructive migration.
folder URI; after the import, inbox mode waits for a folder to be chosen.
Reference
The fork linked in #95 has an implementation with a Room entity, a snapshot
helper covering 37 preference keys, a bottom-sheet picker and SAF import and
export. It is a reference for scope, not a base for the code.