android: shows and attaches files, and the share sheet takes images
CI & Build / Build now, or wait for Android? (push) Successful in 2s
Android / Build, or is the channel already serving this? (push) Successful in 3s
CI & Build / Python lint (push) Successful in 1s
CI & Build / Web typecheck and unit tests (push) Successful in 11s
CI & Build / Python tests (push) Successful in 12s
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 2s
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Skipped
Desktop (Tauri) / Tauri desktop (Linux) (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Skipped
Desktop (Tauri) / Update manifest (push) Skipped
CI & Build / integration (push) Successful in 45s
CI & Build / Build & push image (push) Skipped
Android / Kotlin + Rust (APK) (push) Successful in 11m6s

The phone downloaded every attachment and drew none of them, so a photo note
looked empty. Now:

- Cards show a note's first image and name its other files; the editor shows
  every image at full width and every file as a row. Tapping one opens it in
  whatever app handles its type (a cache copy under its real name, through a
  FileProvider that serves only those copies). Each can be removed, and a file
  the server refused says why under it.
- The editor's toolbar has an Attach button (any type, several at once). Files
  are stored on the phone straight away and upload on the next sync that
  reaches a server, through the core's step-6 path. Link previews show in the
  editor too, and can be dismissed.
- Share → Inkwell accepts one or several images, with or without a caption,
  finishing #1899's deferred image/* target.
- The FFI gains add_attachment, delete_attachment, delete_preview and
  blob_path. The sync summary counts uploads and failed uploads.

Images decode at the size they are drawn (BitmapFactory sampling plus EXIF
rotation, small LRU cache), so no image library is added. Files over 50 MB are
refused on the phone before they are read whole into memory.

Task #5169.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 10:59:36 -04:00
co-authored by Claude Opus 5.5
parent 5b11490858
commit ac4427f834
16 changed files with 1062 additions and 36 deletions
+31 -5
View File
@@ -125,17 +125,27 @@
Capture without opening the app first: Share → Inkwell from
anywhere, and the selection toolbar in any text field.
text/plain ONLY, and image/* deliberately absent. Nothing in this
app can create an attachment — the core has `delete_attachment` and
no counterpart, and the FFI exposes neither. Claiming images in the
share sheet would put this app in front of people for a job it
cannot do and fail after they had chosen it.
Text, and images one at a time or several at once. A shared image
becomes a note carrying it as an attachment, stored on the phone
and uploaded on the next sync that reaches a server (#5169).
image/* rather than */*: a photo or a screenshot is what people
share into a notes app. Claiming every type would put Inkwell in the
share sheet for APKs, contacts and calendar entries, where it would
be noise. Other files attach from inside the editor, whose picker
takes any type.
-->
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<!--
The label is what appears in the text-selection menu beside Copy and
@@ -164,6 +174,22 @@
PackageInstaller. Without it a failed install would be indistinguishable
from someone declining the dialog (Scribe #2438).
-->
<!--
Hands an attachment to the app that opens its type. Not exported, as a
FileProvider must not be: access is granted one URI at a time, on the
intent that opens it. It serves only the cache copies listed in
res/xml/file_paths.xml, never the note store.
-->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.files"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<receiver
android:name=".UpdateReceiver"
android:exported="false" />