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 · 18h
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 · 22h
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
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 · 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 · 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
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
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 · 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 · 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
Verdetto @verdettoqr.com · 21/09/2026
A workflow of ours broke on a colon. In YAML, run: echo "status: ok" is a parse error: an unquoted value can't hold a colon and a space, inner quotes or not. In a list, - Note: text quietly becomes a mapping. Quote the whole value, or use run: | #YAML #GitHubActions
A card titled YAML: a colon and a space end the string, with three examples. run: echo "status: ok" is a parse error, mapping values are not allowed here; the inner quotes do not count, because the value does not start with one. A list item - Note: check the logs gives no error at all: it quietly becomes a mapping. The fix: quote the whole value, run: 'echo "status: ok"', or use a block scalar, run: |. Footer: checked with PyYAML 6.0.3; we parse every workflow file as its own step before a push, and the step stops on failure.
010
Verdetto @verdettoqr.com · 21/09/2026
The product sheet, before and after: sections in cards, amounts in a table, a connected button group for per serving or per 100 g. We still need testers. Join the list: groups.google.com/g/verdetto-testers Then opt in: play.google.com/apps/testing/com.ve… #AndroidDev
Two phone screens side by side under the words A product lookup, before and after. Both show a barcode scanner's product sheet for the same cereal, Post Cocoa Pebbles, EAN-13 0 884912 129659, with the Nutrition section open. Left, labelled 1.0.0: plain section headings, two separate chips for per serving and per 100 g, a green ring chart reading 133 kcal, and the nutrients as a loose list. Right, labelled In test now: each section in its own lighter card, the Nutrition heading previewing 140 Cal per serving, per serving and per 100 g as one connected button group, a ring chart in blue, brown and purple reading 140 kcal, and the amounts right-aligned in a table with a rule between the rows, then a group headed Of the fat.
020
Verdetto @verdettoqr.com · 21/09/2026
Moved the app to Material 3 Expressive over the weekend. Left 1.0.0, right in test now: the warning and both choices share one card, and the way out is the filled button. Trap: Button's shapes overload defaults to the small padding, 16 dp not 24. Name ButtonDefaults.ContentPadding. #AndroidDev
Two phone screens side by side under the words Before and after: Material 3 Expressive. Both show a QR scanner's result sheet over the camera after a scan of a code whose address, paypa1.com, imitates paypal.com. Left, labelled 1.0.0: a pale red banner reads Imitates paypal.com, and under it a wide outlined button reads Open anyway, above a row of five round actions. Right, labelled In test now: the address shows its path, and one card with a red line around it holds the warning, a plain text action Open anyway and a filled Close button, above four round actions. The code's outline in the camera view is red in both; on the right it has a dark edge and a light fill.
010
Verdetto @verdettoqr.com · 20/09/2026
Is try/except around list.index slower than checking with in first? Timed on CPython 3.12. Found: try wins at every size, about 2x on long lists, since checking first scans twice. Missing: the raise is a fixed cost, 4x at ten items, 16 percent at a thousand. #Python #Programming
A bar chart titled Python list.index when the item may be missing. CPython 3.12.10, timeit, best of five, nanoseconds per lookup, three ways: in then index, try/except, and a sentinel appended to the list. Ten items, found: 67, 52 and 80 ns; missing: 56, 235 and 95 ns. One thousand items, found: 3,034, 1,599 and 1,644 ns; missing: 2,943, 3,407 and 3,274 ns. Caption: found, try wins, by about 2x on the long list, since checking first scans the list twice; missing, the raise is a fixed cost, roughly 200 to 450 ns here.
000
Verdetto @verdettoqr.com · 20/09/2026
Building someone's Rust CLI from a clone: cargo build --release already made the binary, in target/release/. On stable, cargo install --path . ignores the repo's Cargo.lock unless you pass --locked. From cargo 1.100 the lockfile is the default: the fix merged on 11 September. #RustLang #Rust
A card titled Building a Rust command-line tool from a clone, with three commands. cargo build --release: the binary is already there, in target/release, it just is not on PATH. cargo install --path . : copies it into ~/.cargo/bin; on stable cargo today, 1.98, it ignores the repo's Cargo.lock, so dependencies are resolved again, possibly to newer versions. cargo install --path . --locked: the same, built with the versions in Cargo.lock, the ones the author tested; from cargo 1.100 that is the default. Footer, quoting the stable Cargo Book on cargo install: By default, the Cargo.lock file that is included with the package will be ignored. The nightly book already says the opposite: the fix merged on 11 September 2026.
000
Verdetto @verdettoqr.com · 19/09/2026
The same address, twice: as typed, then in capitals, and the second is the smallest size a QR code comes in. QR's compact alphabet is from 1994 and has no lowercase: a capital costs 5.5 bits, anything else 8. Domains ignore case. Paths usually don't. Party trick, or has anyone used it?
Two QR codes side by side on white, both black. Left: the address https://verdettoqr.com as typed, a 25 by 25 module code. Right: the same address in capitals, HTTPS://VERDETTOQR.COM, a 21 by 21 module code, one size smaller. Under each code its address and its module count.
000
Verdetto @verdettoqr.com · 17/09/2026
Verdetto's closed test is open. A scanner that shows you the link before it opens, with no ads. Join the testers list: groups.google.com/g/verdetto-testers Then opt in here: play.google.com/apps/testing/com.ve… Android 8 and up, 14 days, feedback welcome.
000
Verdetto @verdettoqr.com · 12/09/2026
The benchmark page is up: every code we tried, what came back, what came back wrong, and how long it took. In our desktop tests, 471 of 514 codes in a hard public photo set read, none wrong. 768 damaged QR codes, every ladder shown, zero wrong reads. verdettoqr.com/how-we-test
000
Verdetto @verdettoqr.com · 09/09/2026
paypa1.com, with a one where paypal.com has an L. I made that code to see what the app does with it: the whole address first, a chip that says it imitates paypal.com, and nothing opens until I choose. The name just before the .com is where these hide, so read that one first.
Verdetto result sheet, light theme. The address paypa1.com is shown in full with /login under it. A caution chip reads Imitates paypal.com. Below it an Open anyway button, then Copy link, Share, Search and Raw. Nothing has opened.
000
Verdetto @verdettoqr.com · 07/09/2026
Hello. Verdetto is a free QR and barcode scanner for Android with no ads and no fake buttons. It shows the link before it opens, checks it for warning signs on your phone, and reads damaged codes. It never says anything is safe. Closed test soon; reply or write to support@verdettoqr.com.
000