Skip to content

fix(android): check item text before URI and avoid coercing URI items to text - #31

Merged
thtbee merged 2 commits into
thtbee:mainfrom
jochenkirstaetter:fix/android-clipboard
Oct 19, 2026
Merged

thtbee merged 2 commits into
thtbee:mainfrom
jochenkirstaetter:fix/android-clipboard

Conversation

@jochenkirstaetter

@jochenkirstaetter jochenkirstaetter commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

What and why

Addresses #27:

  • In PhoneClipboard.selectClipItem, check item.text before item.uri so clipboard items that carry both text and a URI keep their text rather than being misclassified as images.
  • Only fall back to ClipDescription's image MIME hint (descHasImage) when ContentResolver.getType(uri) returns null, so non-image typed URIs (e.g. application/pdf) are not treated as images.
  • Avoid calling coerceToText on URI-backed clipboard items so Android does not stream raw binary content (or turn non-image URIs into raw content addresses).

How it was tested

  • Unit tests in PhoneClipboardTest.kt covering PhoneClipboard.selectClipItem across text+URI items, resolver-typed and untyped image URIs, non-image typed URIs (application/pdf with descHasImage both false and true), and coercedText fallback for URI-less items.

Checklist

  • ./scripts/check.ps1 passes (or the CI equivalent for the parts I changed)
  • User-facing strings are translatable, and new UI works with keyboard and screen readers
  • No personal content (notification text, messages, file names, keys) is logged
  • Commits are signed off (git commit -s), see CONTRIBUTING.md

@jochenkirstaetter jochenkirstaetter changed the title fix(android): retry transient empty clipboard reads and add theme/loc… fix(android): retry transient empty clipboard reads Oct 18, 2026
@jochenkirstaetter
jochenkirstaetter marked this pull request as ready for review October 18, 2026 20:57

@thtbee thtbee left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for splitting this out. The .filter { it.uri == null } before coerceToText is exactly the fix for #27.

A few things before it goes in:

  1. isImageUriByMagic opens the file inside read(), which runs from SendActivity.onWindowFocusChanged on the main thread. A slow provider (a cloud file, or a big file on an SD card) can stall the UI there. When getType doesn't say it's an image, treating it as not an image is fine: with the filter above, the worst case is that nothing gets sent, not garbled text. So I'd drop the sniffing, here and in readImage, where BitmapFactory already covers other formats.
  2. The URI is now checked before the item's text. An item can carry both, and descHasImage looks at the whole clip rather than that item, so a text item in a clip that also holds an image would go out as an image. Checking the text first, as before, avoids that.
  3. The title says it retries transient empty reads, but I don't see a retry in the diff. Could you update the title, or add the missing part?

For a test, there's no Robolectric in the project, so pulling the decision (text, URI, MIME type) into a small pure function and testing that with JUnit would cover the actual bug better than the header checks.

@jochenkirstaetter jochenkirstaetter changed the title fix(android): retry transient empty clipboard reads fix(android): avoid coercing non-text clipboard URIs to file paths Oct 18, 2026
Signed-off-by: Jochen Kirstätter <7329802+jochenkirstaetter@users.noreply.github.com>

@thtbee thtbee left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks, this reads really well now. selectClipItem as a pure function with those tests is a nice way to pin the bug down, and the text-first order and no sniffing are exactly what I hoped for.

Two small things, then I'm happy to merge:

  1. descHasImage still wins when Android knows the item isn't an image. In a clip with an image and a PDF, the PDF would come back as Image and fail later as unreadable. Using it only when the type is unknown keeps your untyped-image case working: mime?.startsWith("image/") == true || (mime == null && descHasImage).
  2. The description still mentions retries and sniffImageMime. Could you update it to match? It ends up as the record of the change.

When ContentResolver returns a non-image MIME type (e.g. application/pdf) for a
clipboard URI, do not let descHasImage override it in selectClipItem.

Signed-off-by: Jochen Kirstätter <7329802+jochenkirstaetter@users.noreply.github.com>
@jochenkirstaetter jochenkirstaetter changed the title fix(android): avoid coercing non-text clipboard URIs to file paths fix(android): check item text before URI and avoid coercing URI items to text Oct 19, 2026

@thtbee thtbee left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks, this looks right now. Text first, the image hint only when the resolver has no type, and no more coercing URIs. The tests cover the cases I was worried about.

@thtbee
thtbee merged commit a56bcaf into thtbee:main Oct 19, 2026
6 checks passed
@thtbee thtbee mentioned this pull request Oct 19, 2026
@jochenkirstaetter
jochenkirstaetter deleted the fix/android-clipboard branch October 19, 2026 05:01
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