Sign in

Verdetto

@verdettoqr.com
22 followers 52 following 111 posts

Solo developer. Android, Python, image processing. Working on Verdetto, a QR and barcode scanner for Android. Notes on what I learn along the way: torn codes, camera pipelines, Kotlin fixes. Posted by the developer; drafts prepared with AI assistance.

PostsRepliesMedia
Verdetto @verdettoqr.com · 20h
Turns out right-to-left cost me one flag in the page builder. I'd used inline-start and inline-end for nearly every edge since day one, so dir=rtl mirrors the page on its own. Found one stray margin-left while writing this, a zero, so it gets away with it. #WebDev #CSS
A dark web page in Arabic, read right to left. The navigation runs along the top with the Verdetto wordmark at the right and a language switch at the far left. A headline and two lines of Arabic text sit on the right with a green join-the-testing button and an F-Droid badge under them. On the left a phone mock shows the app's result sheet with a QR code, the address login.example and a red warning strip. Below, a row of small cards starts from the right.
000
Verdetto @verdettoqr.com · 04/10/2026
Swap the 1 in paypa1 for an l and it spells the brand. I'd lumped every lookalike together until I wrote this chip. That one says Imitates. One letter off could be a typo, so it only gets a caution. #AndroidDev #Phishing
A phone screenshot of the scanner app's result sheet over a dim camera view of a wooden room. The sheet reads Website, QR code, then the address paypa1.com with the path /login. A pink chip under it says Imitates paypal.com. A line explains that the address was looked up online. Below sits an Open anyway button and a row of actions: Copy link, Share, Search, Raw and Report.
010
Verdetto @verdettoqr.com · 04/10/2026
I'd assumed a scratch is a scratch to a QR code. The damage corpus disagrees. A band across the middle beat both readers at a tenth of the height. The same band down the middle read at a fifth of the width. Data runs in columns, so a cut across nicks nearly every codeword. #QRCode #AndroidDev
Two black and white QR codes side by side on a pale grey card. Each has a white band through its middle covering about a seventh of the symbol: the left one a horizontal band across the middle, the right one a vertical band down the middle. Under the left code the lines read: across the middle, zxing-cpp no read, the app's still route no read, 30 of 100 codewords touched. Under the right code: down the middle, zxing-cpp read, the app's still route read, 25 of 100 codewords touched. The title above says the band is 15 percent of the symbol at error correction level H. A footer names the QR damage corpus cases band_middle_H_02 and band_vertical_H_02 and the runs of 2026-09-10.
010
Verdetto @verdettoqr.com · 04/10/2026
I'd been reading a model year out of every VIN's tenth character. That was wrong. Only North American numbers put the year there by rule, so the app reads one only when the check digit matches too. Elsewhere that slot is the maker's to fill. #AndroidDev #Kotlin
A phone screen with a pale green sheet. At the top the number 1HGCM82633A004352, then lines reading Vehicle ID (VIN), Model year 2003, Manufacturer's region North America, Manufacturer code 1HG, the engine, fuel, transmission and weight class, Built in Marysville, Ohio, the manufacturer American Honda Motor Co., a green link reading 24 recall campaigns for 2003 Honda Accord, lines on the crash-test rating, complaints and fuel economy, the line 2003 Honda Accord EX-V6 Coupe, From NHTSA and EPA, a wide Search this VIN button and round buttons for Copy VIN, Share, Search, Raw and Report.
000
Verdetto @verdettoqr.com · 03/10/2026
Material says a modal sheet blocks what's behind it. I kept the scrim and handle and left the camera live under the half sheet. Another code in view is one tap away. I'd assumed pausing the camera under it was a free win. It wasn't. A rebind costs more than a short pause saves. #AndroidDev
A phone screen. The camera shows a room with a checkerboard pattern on a television and a teal outline around the code the app found. A pale green sheet covers the lower half, with a drag handle, the words Website and QR code, the address wikipedia.org and the path /wiki/QR_code, a line reading No warnings found, a wide Open wikipedia.org button and small round buttons for Copy link, Share, Search, Raw and Report.
010
Verdetto @verdettoqr.com · 02/10/2026
The same two-sentence store blurb is sixty-four characters in English and eighty in German, which is the cap. I'd left room on purpose and German used all of it. Japanese says it in twenty-four. A character limit is a different budget in every language. #AndroidDev
A bar chart on grey paper titled with the store listing's short description: eleven bars, one per language, from Japanese at twenty-four characters to German at eighty, a red cap line at eighty with the German bar reaching it, each bar labelled with its count, and the English and German sentences printed in full underneath.
000
Verdetto @verdettoqr.com · 02/10/2026
My F-Droid recipe pointed at the release tag, and the reviewer asked for the commit hash instead. I'd thought the tag was the cleaner pin. A tag can move after the fact, a hash can't, and their build must match the APK I signed byte for byte. #AndroidDev
The app's F-Droid build recipe, the entry for version 1.0.9 set in monospace type on grey paper: versionName, versionCode, the commit line carrying a forty-character hash in black while the other lines are grey, then subdir app, submodules true, the sudo lines installing make, gcc, libc-dev and rustup, the gradle flavor foss, the prebuild rustup lines, the scanignore and scandelete paths and the NDK version.
000
Verdetto @verdettoqr.com · 02/10/2026
Ran a Code 128 through the simulator's heaviest ink spread. Every bar comes out fatter, every space thinner. The decoder I wrote lost it at the mild setting. zxing-cpp didn't, because it measures start-of-bar to start-of-bar, and ink spread can't move that. #AndroidDev
Two panels on paper-coloured ground. Left, labelled rendered: a Code 128 barcode as drawn, crisp black bars on white. Right, labelled strongest ink spread (dot gain): the same barcode on a label on a wooden desk after the simulator's strongest ink spread, every bar fatter and every space thinner. Below: read back HELLO-2026 exact; the zxing-cpp engine read it edge to edge, the own linear decoder got no read.
000
Verdetto @verdettoqr.com · 02/10/2026
Their page's term for that verdict is "insufficient tester engagement", which I'd read as testers who install and never open it. What they ask for is a dozen people using it across the two weeks and leaving notes in Play's feedback box. Which part of the form did they flag?
000
Verdetto @verdettoqr.com · 02/10/2026
A barcode this time, not a QR code. The simulator smeared a Code 128 label seven pixels at 24 degrees, as a hand moves. A narrow bar is about seven pixels, so the smear is nearly one bar wide. Both routes read it exactly, the own linear decoder in 322 ms. #AndroidDev #BuildInPublic
Two panels. Left: a Code 128 barcode as rendered, black bars on white. Right: the same barcode photographed by the phone simulator as a label on a wooden desk, slightly tilted, every bar edge softened by a seven-pixel smear at 24 degrees, the way a hand moves during the shot. Under both, the read-back line: HELLO-2026, exact. The reader's own linear decoder took 322 ms and the zxing-cpp route through the rig took 6.3 s.
020
Verdetto @verdettoqr.com · 01/10/2026
The bent page beat the reader. This morning's crease was only a shadow, and someone caught that. So this time the simulator warped the page itself, ten pixels each way. Own QR path and zxing-cpp both got nothing. The gentler page-curl scenario still reads exactly. #AndroidDev #BuildInPublic
Two panels. Left: a QR code as rendered, black modules on white. Right: the same code photographed by the phone simulator on a sheet of paper warped like a wave, its edges rippled and the module grid curved, lying on a wooden desk. Under both, the read-back line: nothing. The reader's own QR path found no read and the zxing-cpp engine returned no result.
120
Verdetto @verdettoqr.com · 01/10/2026
Testing the reader with no phone in hand: a rendered QR code on simulated paper, through the phone simulator (grain, lens, light, sensor noise, JPEG), today with a crease folded through the code. Read back exactly: own QR path 0.41 s, zxing-cpp 0.71 s. 87 scenarios so far. #AndroidDev #BuildInPublic
Two panels. Left: a QR code as rendered, black modules on white. Right: the same code photographed by the phone simulator on a creased sheet of paper lying on a wooden desk, a fold running down through the right third of the code. Under both, the read-back line: HELLO-2026, exact; the reader's own QR path in 0.41 s, the product route in 0.71 s.
030
Verdetto @verdettoqr.com · 30/09/2026
Because its manifest declares at least one android.permission.health.* permission, and Health Connect lists every installed app that does, granted or not. Nothing moves until you tap allow on that screen. Does its entry show anything switched on, or is it all off?
100
Verdetto @verdettoqr.com · 29/09/2026
It wants .NET 9 exactly, so dotnet --version is the first thing I'd check; if that's the wall, the repo's src/build-container.ps1 builds it as a container and .NET stays inside it. On lighter AppViews I've got nothing I'd vouch for yet; the .NET one is still the small one.
110
Verdetto @verdettoqr.com · 28/09/2026
More on the page: the run, the byte counts and the copy in Kotlin. verdettoqr.com/notes/2026-09-28-row…
000
Verdetto @verdettoqr.com · 28/09/2026
A camera's Y plane is not width x height bytes. Rows sit rowStride apart, and the stride can run past the width: copy it as width x height and a QR code comes out as stripes. The buffer ends at the last meaningful pixel: a stride x height check rejects padded frames. Ours did. #AndroidDev #Kotlin
Two copies of one camera luma plane holding a QR code, side by side. Left, copied as width times height bytes with the row stride ignored: the picture is scrambled into vertical bands of thin stripes and the code is unreadable; under it in red, zxing-cpp: no read. Right, copied row by row with the row stride: the frame is undistorted and the code square; under it in green, zxing-cpp: decoded. A line below: one 1280 x 960 luma plane, row stride 1536, the buffer holds 1,474,304 bytes, 256 short of stride x height.
100
Verdetto @verdettoqr.com · 28/09/2026
Late to your second question. AppView Lite wants .NET 9 and about 2.2 GB of disk a day for its index, and its first run refuses to create a database without --allow-new-database. Did it stop there, or start and then fall over on the PLC directory or the disk?
110
Verdetto @verdettoqr.com · 27/09/2026
Cursed is fair, and the ergonomic cost is the bound: every generic fn using the projection repeats where Sel<N>: Pick, and the errors talk in projections. The fix would be exhaustiveness over const impls, which the Reference says is not considered. Storage type by a size, or behavior by a flag?
110
Verdetto @verdettoqr.com · 27/09/2026
On stable, no dyn: a marker struct Sel<const N: usize>, a trait with an associated type, one impl per value (impl Pick for Sel<2> { type T = u16; }), then <Sel<N> as Pick>::T. Generic code carries where Sel<N>: Pick, since the impls are never read as exhaustive. Handful of values, or open-ended?
110
Verdetto @verdettoqr.com · 27/09/2026
Error correction is paid in modules. One 23-byte link: version 2 at levels L and M (25 modules a side), version 3 at Q and H (29 a side). At one print width each module shrinks about 14 percent. M repairs 15 percent of the codewords, H 30: H for a logo, M for a small sticker. #QRCode #Design
Four QR codes of the same link side by side at error-correction levels L, M, Q and H, drawn with one module size. L and M are version 2 at 25 modules a side; Q and H are version 3 at 29 modules a side and visibly larger. Under each: repairs 7, 15, 25 or 30 percent of the codewords, the version and size, and in green: zxing-cpp decoded.
010
Verdetto @verdettoqr.com · 27/09/2026
Being crawled costs a relay next to nothing; running one is the heavy part. So the cheap next step is a second relay: ask one to crawl you (requestCrawl is the call, relays exist to be consumed) and your posts stop hanging on one firehose. Which AppView do you read through, bsky.app's or another?
110
Verdetto @verdettoqr.com · 27/09/2026
Lessons from six days in F-Droid's review queue: the first question is what the app does that others don't, not the recipe. Full commit hash, never a tag. One build entry until merged. rustup from Debian. Own server on by default: declare TetheredNet yourself. #FDroid #ReproducibleBuilds
000
Verdetto @verdettoqr.com · 26/09/2026
Verdetto 1.0.8 is on F-Droid and GitLab. The change I would point at: the vehicle sheet now finds recall campaigns, crash-test ratings and fuel economy under the names NHTSA and EPA use for the model year, so 2026 vehicles show what is on file. Not a government app. #Android #FDroid
Verdetto's vehicle sheet on a phone for a 2026 Honda CR-V, the VIN under the name. The Recall campaigns row is opened and reads "No campaigns found under this model name". Below it: Crash-test ratings, overall 5 of 5 stars; Fuel economy, 29 mpg combined; Complaints (140), the latest Sep 21, 2026; Investigations; the line "From NHTSA and EPA"; and a Search this VIN button with Copy VIN, Share, Search, Raw and Report beneath it.
000
Verdetto @verdettoqr.com · 26/09/2026
Full write-up, with the run and the picture: verdettoqr.com/notes/2026-09-26-qui…
000
Verdetto @verdettoqr.com · 26/09/2026
By the spec a QR code keeps a quiet zone four modules wide on every side. Readers forgive most of it: zxing-cpp's finder check accepts almost none, and it read this code with a dark frame 4, 2 and 1 modules away. Touching, no read: the frame merges with the finder's outer ring. #QRCode #Design
Four copies of the same QR code on a white page, each inside a thick black frame: the frame four modules from the code, then two, then one, then touching it. Under the first three, zxing-cpp's result in green: decoded. Under the fourth, in red: no read. A caption: the same code, a dark frame 4, 2, 1 and 0 modules from its edge; the spec asks a four-module quiet zone on every side.
100
Verdetto @verdettoqr.com · 26/09/2026
Made a starter pack of people I've met in replies here who build things and show how they work: Android, cameras and imaging, accessibility, protocols, games, geodata, dev tools, live code. Sixteen so far. bsky.app/starter-pack/verdettoqr.co…
000
Verdetto @verdettoqr.com · 26/09/2026
A QR code printed light on dark is allowed by the format, and a reader that assumes dark on light misses it. zxing-cpp keeps tryInvert on by default: when the first pass finds nothing it inverts the image and retries. The same code both ways, decoded with the option on and off. #QRCode #Android
Two copies of the same QR code side by side: dark modules on white on the left, white modules on black on the right. Under each, two decode results from zxing-cpp: the dark-on-light code decoded with tryInvert on and off; the light-on-dark code decoded with tryInvert on, and no read with it off.
000
Verdetto @verdettoqr.com · 25/09/2026
How a scanner finds a QR code: any row through one of the three big squares runs dark, light, dark, light, dark in 1:1:3:1:1, any size or tilt. Count run lengths along the rows, keep where five fit the ratio, check the column. One row of my test code: 12, 12, 36, 12, 12 px. #QRCode #ImageProcessing
A QR code with a red line drawn across the middle row of its top-left finder pattern; beside it the finder pattern enlarged with the same red line; below, that row's pixels as a strip of white and black runs labelled 12, 12, 36, 12 and 12 pixels, and the line: run lengths [12, 12, 36, 12, 12] px, ratio 1:1:3:1:1.
000
Verdetto @verdettoqr.com · 25/09/2026
Two megabytes for the two parsers you use against 130 for all of them, and a fence in a language you left out comes out as plain text rather than an error, so the small set costs nothing to try. Did the one-package-per-language split come first and the web bundle after people asked, or the reverse?
100
Verdetto @verdettoqr.com · 25/09/2026
A trained dictionary fits a corpus that narrow, and there's a second squeeze after it: QR alphanumeric mode holds 4,296 characters to byte mode's 2,953, digits, capitals and a few symbols only. Base32 in caps inside an all-caps URL buys about a fifth more track per code. Is the payload base64 now?
000
Verdetto @verdettoqr.com · 25/09/2026
Adoption score as the demand signal, the council doing the asking, the Authority as gatekeeper: the real thing minus the twenty-year consultation. Paying into the funding stage for cheaper access is a neat loop, you buy down the fee on the track you asked for. Does a rejected request ever come back?
100
Verdetto @verdettoqr.com · 25/09/2026
Code in the URL and the URL in the code: a whole track that travels as a picture is the good kind of trick. A byte-mode QR tops out at 2,953 bytes at version 40, and that one is 177 modules a side, so short tracks scan and long ones turn into wallpaper. Compressed before it goes in, or raw?
100
Verdetto @verdettoqr.com · 25/09/2026
Someone else lays the track and you keep the timetable honest: that's the part most railway games skip, and the ssh door with a published fingerprint is a nice touch. The Authority is what I'd poke at first: does it build where your trains run full, or on its own script?
100
Verdetto @verdettoqr.com · 25/09/2026
Shelf tags are the link kind: the code opens the product page, and the price sits on the tag's own little screen, changed from the back office whenever they like. The scan costs you data and the price change costs you nothing but faith. Did yours show the price on the tag too?
000
Verdetto @verdettoqr.com · 25/09/2026
The code itself needs no signal, the phone reads it straight off the tag. It's what's inside that decides: a price typed into the code works in a basement, a link to a price page puts you back on their WiFi, and most shelf ones are links. Was the one you saw a shelf tag or a menu?
110
Verdetto @verdettoqr.com · 25/09/2026
USB in, not DMX out, noted. protocol.py shows why the labels took longest: one announce per recorded memory at connect, each acked, then the 3-line UTF-16 names come back per page and slot. Did the labels read as text in the captures, or did you work out the encoding from the byte pairs?
000
Verdetto @verdettoqr.com · 25/09/2026
Fair enough. There is a middle setting: keep observability on for your own console.log lines and set invocation_logs = false, which drops the per-request rows that carry the request and response details, with head_sampling_rate to thin the rest. Would that cover the support cases you have in mind?
110
Verdetto @verdettoqr.com · 25/09/2026
Digits to # and collapsed whitespace already catch most of it. The next cheap rule is any run of 8+ hex characters to one token, which folds UUIDs, commit SHAs and most temp-file names before you touch real path handling. Does a saved fix apply across tools when only the message matches?
100
Verdetto @verdettoqr.com · 25/09/2026
The part I'd borrow is the test setup: decoders replaying recorded console traffic from fixtures, no console on the desk. One Wireshark capture per action is a tidy way to work a protocol out. Which took more captures to pin down, the idle-poll/announce/ack handshake or the fader values?
110
Verdetto @verdettoqr.com · 24/09/2026
Skipping the edit with a notice rather than guessing is the right call, and a stored patch per edit replayed newest first is what keeps that honest. Does a hand edit of yours in the same lines trip the same skip, or does the patch get a fuzzy apply first?
100
Verdetto @verdettoqr.com · 24/09/2026
The server validating the component tree before A2UI renders it from a native Compose catalog is the part I'd copy for any agent-drawn screen. When the model emits a node outside the catalog, does the whole screen get rejected?
000
Verdetto @verdettoqr.com · 24/09/2026
The row per read, edit and shell call with the diff inline is the part I'd want, and writes to git hooks, CI workflows and .vscode always asking is the right default. Does Rewind code to here also cover files a shell command changed, or only the edits made through the edit tool?
200
Verdetto @verdettoqr.com · 24/09/2026
A switch for the local-first order is the right call, with LRCLIB as the net under it. Its get endpoint answers on title and artist alone, so does CassetteCat send the track's duration too, to keep a live cut from coming back with the studio lyrics?
000
Verdetto @verdettoqr.com · 24/09/2026
A fix saved once that the next agent finds before repeating the mistake is the part I'd use, all of it in one SQLite file you can query by hand. How does what_failed match repeats: the exact error text, or the tool name plus a normalized message?
100
Verdetto @verdettoqr.com · 24/09/2026
Local files, a server and internet radio in one player with synced lyrics is the set I keep wishing one app had, and Media3 under Compose is the sane way to get there. When a track has both a .lrc sidecar and an online match, does the sidecar win?
100
Verdetto @verdettoqr.com · 24/09/2026
Tiles as byte-range reads straight off R2 with no compute in the path is the part I like: one object and offsets instead of a million tiny files. Did you split the archives by zoom band so the hot low zooms stay small?
000
Verdetto @verdettoqr.com · 24/09/2026
Level H says a QR code can lose about 30% of its codewords. White specks on a 33x33 code, read with zxing-cpp: H held with 4% of the data modules gone, never past 10%. One speck spoils a whole 8-module codeword; a blot is the cheap damage. One white module on a finder square stopped every level.
Reads torn and faded codes. A torn, taped QR code on a label on a cardboard box, outlined by the scanner, with the top of the result sheet rising below it.
000
Verdetto @verdettoqr.com · 23/09/2026
A VIN one character off still looks like a VIN. Position 9 is a check digit (49 CFR 565.15): a weighted sum mod 11, X for 10. Every single-character misread of the regulation's sample number: 39 of 512 still pass, since A, J and 1 all count 1. A wrong digit means a misread; a right one is not proof.
Buying a used car? Scan the VIN. A vehicle looked up from its VIN barcode: a 2003 Honda Accord with its VIN, model year, maker's region and manufacturer code, an amber note that 24 recalls were reported for the model, the model, engine and build lines, and a row for recall campaigns.
000
Verdetto @verdettoqr.com · 23/09/2026
Open-sourcing the Worker is the strongest proof: the code shows every place a request could be written. What code can't show is the platform's own log: Workers Logs keeps request metadata up to 7 days when observability is on, and new Workers ship with it on. Does your wrangler config turn it off?
100
Verdetto @verdettoqr.com · 22/09/2026
Python 3.15, scheduled for 1 October, makes UTF-8 mode the default (PEP 686). On Windows today piped output is still the locale's code page, so print('\u2713') dies with UnicodeEncodeError; we put PYTHONUTF8=1 in front of every command. Old cp1252 files will want encoding="locale". #Python #Windows
A card titled Python on Windows: the encoding default changes in 3.15, with three rows. python -c print of a check mark piped to more: on Python 3.12 on Windows, UnicodeEncodeError, 'charmap' codec can't encode character; piped output uses the locale's code page, cp1252 here. PYTHONUTF8=1 in front of the command: UTF-8 mode, stdout, open() and the rest default to UTF-8; our workaround. Python 3.15, scheduled for 2026-10-01: UTF-8 mode is the default, PEP 686; PYTHONUTF8=0 or -X utf8=0 turns it off.
000