Fills in `image_cache`, which the previous commit's "On this device" filter read but nothing wrote. Also carries in-flight work that shared these files: the Android TLS root store, the settings page, and a regenerated traceability report. # Two populations, deliberately separate An original is kept here for one of two reasons, and conflating them produces the exact failure the feature exists to prevent. **Pinned** originals were asked for. Pinning a collection before a trip is a promise, so pinned rows are never evicted and never counted against the budget — a cap that could silently delete a pinned trip would make pinning worthless, because it could not be relied on without checking. **Passively cached** originals are a side effect of working: develop already downloads the whole file, so keeping it costs no bandwidth and saves the entire transfer next time. This population is what the budget bounds, evicted least-recently-used, because it otherwise grows until a day of culling fills a disk. Sharing one budget would let a large pin starve the passive cache, or let browsing evict a pin. They are separate. # What was built `dr_catalog::cache` owns the bookkeeping — held tier, size, last use, pinned — and writes the bytes; deciding to download stays with the caller, which is what keeps a crate with no network out of the network's business. Files are written to a temporary and renamed, so a dropped connection cannot leave a truncated file recorded as a complete original. They are named by image id, not filename: `Photos/IMG_0001.CR2` and `Trips/IMG_0001.CR2` are different photographs, and a flat cache keyed on the name would serve one for the other. `spawn_full_fetch` became read-through. A hit is a disk read; a miss stores what it downloads and enforces the budget. A cache that cannot be opened is a miss, not a failure to open the photograph. Pinning writes intent — `tier_desired` — without downloading, so the button responds immediately, and `spawn_pin_fetch` fills it in sequentially afterwards. Sequential because these are tens of megabytes each: the lanes that make the thumbnail sweep fast buy little against one connection's bandwidth and cost a great deal of memory. A pin interrupted by a lost connection resumes from where it stopped. Schema v5 adds `pinned` and `path`. `pinned` is a column rather than something inferred from `pinned_by_rule`, which is ON DELETE SET NULL and so cannot answer for an image whose rule was deleted. A v4 catalog migrates in place; existing rows default to unpinned, the safe direction. The budget and "keep opened originals" come from the settings page rather than a constant, and are applied at startup rather than only on change — a cache capped at 2 GB last session would otherwise spend this one filling to the default. Turning off keeping leaves what is already cached readable: those bytes are paid for, and refusing them would re-download images sitting right there, including pinned ones. Also removes a doubled `#[test]` introduced in the previous commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
59 lines
2.6 KiB
XML
59 lines
2.6 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!--
|
|
DarkRoom Android manifest.
|
|
|
|
Deliberately minimal: this packages the viewer for on-device testing (spike
|
|
S2 needs Adreno and Mali hardware, which no emulator represents). Nothing
|
|
here is a distribution manifest yet — no permissions are declared because
|
|
the library grid reads through SAF, which grants per-tree rather than by
|
|
manifest permission (ARCH §6.9).
|
|
-->
|
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
package="paris.tourolle.darkroom">
|
|
|
|
<!-- Everything the app does with a server needs this: Login Flow v2, the
|
|
WebDAV listing, thumbnail and image fetches. Without it Android refuses
|
|
socket creation outright, and the failure is invisible — no panic to
|
|
catch, no log line, just a worker thread that stops. Storage is the
|
|
separate case that genuinely needs no permission here, because SAF
|
|
grants per-tree at runtime (ARCH §6.9). -->
|
|
<uses-permission android:name="android.permission.INTERNET" />
|
|
<!-- Read before deciding whether a sync may run: FR-NC-6 gates background
|
|
work on unmetered-and-charging, which means knowing the network type. -->
|
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
|
|
<!-- Vulkan 1.1 is what wgpu needs; the API 28 floor is where support is
|
|
dependable (NFR-COMPAT-1). Marked required so an unsupported device
|
|
fails at install rather than at first frame. -->
|
|
<uses-feature
|
|
android:name="android.hardware.vulkan.version"
|
|
android:version="0x00401000"
|
|
android:required="true" />
|
|
|
|
<application
|
|
android:label="DarkRoom"
|
|
android:hasCode="true"
|
|
android:allowBackup="false"
|
|
android:supportsRtl="true">
|
|
|
|
<!-- NativeActivity rather than a Kotlin Activity: android-activity's
|
|
glue loads libdarkroom.so and calls android_main. `android.app.lib_name`
|
|
is how it learns which library to load, and must match [lib].name. -->
|
|
<activity
|
|
android:name="android.app.NativeActivity"
|
|
android:exported="true"
|
|
android:configChanges="orientation|keyboardHidden|screenSize|screenLayout|density|uiMode"
|
|
android:windowSoftInputMode="adjustResize">
|
|
|
|
<meta-data
|
|
android:name="android.app.lib_name"
|
|
android:value="darkroom" />
|
|
|
|
<intent-filter>
|
|
<action android:name="android.intent.action.MAIN" />
|
|
<category android:name="android.intent.category.LAUNCHER" />
|
|
</intent-filter>
|
|
</activity>
|
|
</application>
|
|
</manifest>
|