Photograph six screens of the app, and frame them for the store - #598
Merged
Conversation
andiwand
marked this pull request as ready for review
August 18, 2026 07:14
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 90b0ba858b
ℹ️ 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".
The store copy beside these is written, so it lives in git and the release uploads what is committed. A screenshot is not written, it is taken: it is worth what the build it came off is worth. So these are taken during the release run, from the build going out, framed there, and handed to supply from there. Nothing is committed. Six pictures per device - the recently opened list, a text document with a search running, a spreadsheet, an edit under way, a pdf and a Word file - on a Pixel Pro and a Pixel Tablet, in the fifteen locales the listing is written in. That is 180 pictures a release. OpenDocument.ios landed the same arrangement in #164, and the python here is deliberately close enough to lift out into a shared repository later. What differs is the half that had to be adjusted rather than copied. ScreenshotTests is the whole back door. An instrumented test runs in the app's own process, so laying the samples out, filling the recent list and switching the app's language are all things a test can do directly - where iOS has to carry a ScreenshotMode inside the app because a UI test there has no such reach. There is no new line in MainActivity and nothing test shaped in an apk that ships. Everything past that goes through the app the way a user does: the recent list is documents that were really opened, edit mode is the button, and the find bar is typed into. It writes into gradle's additionalTestOutputDir, which gradle copies back before it uninstalls the apks. getExternalFilesDir is the obvious answer and the wrong one - an app's own storage goes with it when it is uninstalled, so the pictures were written, the test passed, and there was nothing left to fetch. The frame is drawn rather than downloaded, as on the other side, but onto a canvas of its own: play refuses a picture more than twice as long as it is wide, and a Pixel 9 Pro XL is 2.23:1 before anything is drawn around it. The device in it is an Android one - a punch hole rather than a pill, keys on the right edge, tighter display corners - and it fits whole, because the buttons this app puts in the bottom right corner are the ones a reader taps. The documents in them are written here too, in every language, and so are the headlines. Six languages more than the App Store side needs, since the play listing is in fifteen locales and every one of them has an app translation, so none borrows the English. The bundle upload and the listing upload are now separate jobs, as they are on the other side: a bundle cannot go up twice, while the listing stays editable for as long as the release sits on the internal track. A screenshot run that wedges an emulator costs the release its pictures, not its binary. Three things the pictures themselves forced, and each is in a comment where it bit: the search screenshot searches the report and not the invoice, which is the document the word was counted out of; the run turns the device upright by trying rather than by telling, because rotation 0 lays a tablet down; and it refuses anything below Android 15, where the app does not tell the system bars to follow a light theme and every picture gets a white clock on a white bar. The four phone screenshots under fastlane/metadata predate the 4.14 redesign and were never uploaded. They are gone: the release takes its own now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CocwstWzFw7M5rhj83ncUc
The frame was ported from the App Store side and still described Apple's hardware. Three things were doing the work, and the first is most of it: The corner. Apple's is a continuous curve - the curvature eases into the straight edge instead of starting at full bend - and it takes a superellipse exponent around 5. A Pixel's is very close to a circular arc, which is 2. At this size that is not a subtlety, and it was the single reason the drawing read as an iPhone with the logo filed off. The exponent is a layout value now, at 2.2: a touch of easing, because an exact circle reads as a render. The metal. A Pixel Pro's frame is polished aluminium, near enough a mirror, which is a different thing from the turned titanium the ramp was read off. Brushed metal climbs to one broad highlight two thirds of the way in; a polished one throws a narrow specular at the outer edge, drops away hard, and picks up a weaker sheen where the flat turns down to the glass. It is also warm rather than neutral, and reading the two side by side that tint is most of what separates them at a glance. The proportions. The black mask was 60% of the border and the bright edge a sliver, which is a phone from 2018. On a Pixel it is the other way round - less than half black, the rest frame - and the tablet is the other way again, mostly mask with a thin bright edge outside it. And the composition, which was the other half of the report: 18 pixels of the body past the foot of the canvas against 79 down the right hand side. Off the edge is a decision and a hair short of it is a mistake, so the device is now sized to leave a foot rather than by a fraction that happened to nearly fit - which also means a device whose screen is a little taller or shorter than the one the number was picked for still stands on the ground. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CocwstWzFw7M5rhj83ncUc
`speak()` went through `AppCompatDelegate.setApplicationLocales`, which from api 33 on only forwards to the framework's `LocaleManager` - and only once an AppCompat activity has attached itself to the delegate. Called with nothing on screen, which is where it has to be called, it returned having done nothing at all: every locale's set came out with the app in English. The pictures had Portuguese file names over an English "Recent" and "2 hours ago". It sets the locale through `LocaleManager` now, and waits for the new configuration to reach the process rather than launching into the one it was. Portuguese never got that far. The documents are written as `pt-BR`, which is what `make-screenshot-documents.py` calls the language, while both tables reading them said `pt` - so the run would have died on `sample-text-pt.odt` eleven locales in. That table is one table now: `store_screenshots.LOCALES` is checked against the generator's own languages and written into the test apk's assets, and `ScreenshotTests` reads it from there instead of holding a copy. And the rotation, which #598's review asked about: a fixed 2.5s sleep and one photograph, so a display that took longer moved on to the next setting and could run out of settings without ever seeing the portrait it had asked for. The beat stays - asked too early the screen answers with the way up it is leaving - and a poll follows it. Also: `dressed` is marked before the dressing rather than after, so a failure halfway is still undone; `headline()` cannot reach its faces unbound; a mac with neither Hiragino nor PingFang installed falls back to Arial Unicode rather than refusing to draw a CJK headline; the unused `lay_out` and the version argument `uploadBundle` no longer reads are gone; the release's header and the README's job count say six; and the comments that recounted how a number was arrived at have been cut back to what the number does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBGYtacAAsAiA19UJGvdXb
andiwand
force-pushed
the
store-screenshots
branch
from
August 18, 2026 15:40
90b0ba8 to
f25ad3f
Compare
Asked for a language it has no layout for, gboard covers its own keys with a picker - two layouts, `Skip` and `Next` - and the edit screenshot came out of a keyboard being set up rather than of a document being edited. Japanese asks; the other fourteen do not. The class is what is looked for rather than the word: a key is a `FrameLayout` carrying a description, and what the picker puts up is a real `Button` with a word on it, so there is no list of buttons per language and the next thing gboard decides to ask goes the same way. `uiAutomation` has to be told to answer with the keyboard's window at all - it is not the active one - which the dressing does once. And `pick-avd-profile.sh` looked for `avdmanager` on the path, where a runner does not have it: the tablet job of the first dry run died at exit 127 before an emulator was ever started. It is looked for under the sdk now, and a runner with neither takes the newest name unchecked rather than costing the release its pictures - a name that does not exist is refused by the emulator action a minute later, and says so. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBGYtacAAsAiA19UJGvdXb
…hed on A page fitted to the width of a phone is about two thirds of its height, so a document that ends after one page was photographed with a third of the picture showing the backdrop behind it - a third of the .odt, and half of the .pdf and the .docx. Six screens, three of them ending in grey. The length has to come from somewhere that is already written in fifteen languages, so it comes from the figures: - The **report** gains the costs table under the section it is the figures for. No heading of its own - that would be a word to translate fifteen times - and it reads as a quarterly report with its numbers in it, which is what it should have looked like anyway. - The **contract** gains the appendix its own clauses promise, and is set at 14pt rather than 11. The renderer honours neither `w:spacing` on a paragraph nor `w:trHeight` on a row, so the rows and the size are the only length there is to give it; a clause is a paragraph of its own now rather than half of one. - The **invoice** bills for forty lines instead of twenty and breaks onto a second page, which the writer could not do before: it emits a page and a content stream each now, with the column heads written again above the rows that carried over. It is the only one of the three that really paginates - the other two are drawn as one continuous page - so it is also the only one that needed it. Measured on both: every screen of both devices now reaches the foot of the picture, where three of six ended between a fifth and a third short. `odf_row` comes out of `sheet` so the report can set a table with the same cells, which is also the one place the two documents have to agree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBGYtacAAsAiA19UJGvdXb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The store copy is written down in this repository; the screenshots were not. A picture of
the app is worth what the build it came off is worth, so the release run takes its own -
six screens on a phone and a tablet, in the fifteen locales the listing is written in, 180
pictures a release - frames them and hands them to supply. Nothing is committed, and
.gitignoresays so.OpenDocument.ioslanded the same arrangement in opendocument-app/OpenDocument.ios#164, andthe python here is deliberately close enough to lift out into a shared repository later.
What it is
ScreenshotTestsis the whole of it. An instrumented test runs in the app's ownprocess, so laying the samples out, filling the recent list and switching the app's
language are things a test does directly - where iOS has to carry a
ScreenshotModeinside the app. There is no new line in
MainActivityand nothing test shaped in an apkthat ships. Everything past that goes through the app the way a user does: the recent
list is documents that were really opened, edit mode is the button, the find bar is typed
into.
connectedCheckdoes not photograph astore listing nobody asked for, and refuses anything below API 35, where the app does
not tell the system bars to follow a light theme and every picture gets a white clock on a
white bar.
scripts/make-screenshot-documents.pywrites the documents in the pictures, in everylanguage, reproducibly.
frame-screenshots.pydraws the frame - a Pixel, from itspublished dimensions, nothing downloaded - onto a canvas of its own, since Play refuses a
picture more than twice as long as it is wide and a Pixel 9 Pro XL is 2.23:1 before
anything is drawn around it.
store_screenshots.pysays what a full set is and stagesit.
upload moves out of the bundle upload: a bundle cannot go up twice, while the listing
stays editable for as long as the release sits on the internal track. A screenshot run
that wedges an emulator costs the release its pictures, not its binary.
fastlane/metadatapredate the 4.14 redesign and werenever uploaded. They are gone; the release takes its own now.
Taking them by hand
Still to check
The release workflow's new jobs have never run - draft until a full set has been through
CI. The English set was re-taken locally at this head, phone and tablet, on API 36
emulators, and framed: twelve pictures, nothing missing.
🤖 Generated with Claude Code