Skip to content

Trust the core when it says a file cannot be opened - #563

Merged
andiwand merged 2 commits into
mainfrom
trust-the-core-on-broken-files
Aug 2, 2026
Merged

Trust the core when it says a file cannot be opened#563
andiwand merged 2 commits into
mainfrom
trust-the-core-on-broken-files

Conversation

@andiwand

@andiwand andiwand commented Aug 2, 2026

Copy link
Copy Markdown
Member

The problem

A failed CoreLoader load that RawLoader could not pick up ended at LoaderService.kt:174 as onUnsupported, and DocumentFragment offered to upload the file to use.opendocument.app. That server runs the same engine that just said no, so a damaged .odt cost the user a consent dialog, an upload, a progress dialog and a second failure to learn what the core already knew.

Route on what the service already knows

coreLoader.isSupported is odrcore's own format table (SupportedDocumentTypes.isRenderedByCore), asked until now only to log load_odf_error_expected. One new branch:

rawLoader.isSupported   -> RAW
coreLoader.isSupported  -> onError      // the core names it and still failed: the file is what is wrong
else                    -> onUnsupported // the core never claimed it: the online loader may help

OnlineLoader stays, narrowed to what it is genuinely good for - iwork, wordperfect, dxf, postscript. A side effect is that dialog_upload_file ("we aren't able to open this document, because we don't support its format") becomes true; it was not before.

What the user sees

A dialog over the landing screen rather than a snackbar over an empty document view. MainActivity.closeDocument() is no longer private so the failed load can drop back to it.

  • title Couldn't open this file
  • The file may be damaged, or something went wrong on our side. If you think the file is fine, tell us at support@opendocument.app - the address is a live mailto: link
  • CONTACT left of OK; Contact fires ACTION_SENDTO

FileNotFoundException and OutOfMemoryError keep their own snackbars, so "too big / too many pictures" is still its own message. toast_error_generic is gone; its stale translations stay for Crowdin to prune, since ExtraTranslation is ignored in app/lint.xml.

The grey bar under the buttons

The dialog carries its own content view rather than AppCompat's button bar, which showed a grey band. That band turned out to be the framework's default focus highlight: drawn from onDrawForeground, so every view in the hierarchy reads bg=null fg=null while a solid band sits on screen, and it appears whenever a window is out of touch mode. defaultFocusHighlightEnabled is off on everything focusable in the layout.

Worth knowing for the next person: MainTheme is duplicated across values/, values-v29/ and values-v35/, so a theme item added to only one of them silently does nothing on a modern device. Left alone here.

Test

app/src/androidTest/assets/corrupt.odt is a valid ODF zip with content.xml cut in half - the core detects it as odt and then fails on not an xml file. testCorruptODTIsNotOfferedForUpload asserts both halves: the upload dialog does not exist, and the new message is on screen.

spotlessCheck, lintProDebug, testProDebugUnitTest and all 48 instrumented tests pass on a Pixel 9 Pro emulator (API 36).

Not verified on a device

The preserved upload path. It needs a format the core does not claim to reach the app, which requires the CATCH_ALL alias, and pm enable on that component is blocked for shell. The branch itself is unchanged code and OnlineLoaderTest still covers the whitelist.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Q6G6AhhqpUNAsd6iStodKs

A failed CoreLoader load that RawLoader could not pick up ended as
onUnsupported, and DocumentFragment offered to upload the file to
use.opendocument.app. That server runs the same engine that just said no,
so a damaged .odt cost the user a consent dialog, an upload, a progress
dialog and a second failure to learn what the core already knew.

The service already had the distinction it needed: coreLoader.isSupported
is odrcore's own format table, asked until now only to log
load_odf_error_expected. Route on it. A format the core names and still
fails on is the file's fault and the answer is final; onUnsupported is now
reached only for formats the core never claimed - iwork, wordperfect, dxf -
which is what OnlineLoader is actually good for. It also makes
dialog_upload_file ("we don't support its format") true, which it was not
before.

What the user sees instead is a dialog over the landing screen rather than
a snackbar over an empty document view: the file may be damaged or
something went wrong on our side, with the support address as a live
mailto link and a Contact button next to OK. FileNotFoundException and
OutOfMemoryError keep their own snackbars, so "too big" is still its own
message.

The dialog carries its own content view. AppCompat's button bar showed a
grey band that turned out to be the framework's default focus highlight -
drawn from onDrawForeground, so every view in the hierarchy reads
bg=null fg=null while a solid band sits on screen - which appears whenever
a window is out of touch mode. defaultFocusHighlightEnabled is off on
everything focusable in the layout.

corrupt.odt is a valid ODF zip with content.xml cut in half, and
testCorruptODTIsNotOfferedForUpload asserts both halves: no upload dialog,
and the new message on screen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q6G6AhhqpUNAsd6iStodKs

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 66455dc0af

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread app/src/main/res/layout/dialog_broken_file.xml Outdated
Two findings on the first commit.

An upload that does not come back was landing in the catch-all and getting
the broken-file dialog, which closes the document. But an ONLINE failure
says nothing about the file - the network or the server is what failed,
and it is a format the core never claimed in the first place, so the
reopen offer is still the useful thing to do with it. Branch on
result.loaderType and give it toast_error_upload_failed.

The address in the dialog was clickable through autoLink, which uses
TextView's own mailto handler and throws where no mail app exists -
outside the try/catch the Contact button goes through. It is a
ClickableSpan calling the same guarded contactSupport now. A translation
that dropped the address leaves the message as plain text rather than
mislinking it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q6G6AhhqpUNAsd6iStodKs
@andiwand
andiwand merged commit 245add0 into main Aug 2, 2026
2 checks passed
@andiwand
andiwand deleted the trust-the-core-on-broken-files branch August 2, 2026 20:52
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.

1 participant