# Binary Eye reads 21 barcode formats, generates 11, and ships a make target for a rival scanner

> An ad-free Kotlin barcode scanner for Android that hands results to other apps through deep links and the legacy ZXing Intent, wrapped in a Makefile whose adb targets are useful documentation and, in a few places, wrong. MIT licensed, last commit 2026-10-01.

**markusfisch/BinaryEye** — Yet another barcode scanner for Android

- Repository: https://github.com/markusfisch/BinaryEye
- Website: https://play.google.com/store/apps/details?id=de.markusfisch.android.binaryeye
- Stars: 2,401 · Forks: 181
- Language: Kotlin
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/markusfisch-binaryeye

## Twenty-one formats to read, eleven to generate

The format table is the most concrete thing in the repository, and its two halves do not line up. The read list names 21 entries: AZTEC, CODABAR, CODE 39, CODE 93, CODE 128, DATA MATRIX, DX FILM EDGE, EAN 8, EAN 13, ITF, MAXICODE, PDF417, Telepen, QR CODE, Micro QR Code, rMQR Code, RSS 14, RSS EXPANDED, UPC A, UPC E and UPC EAN EXTENSION. The generate list names 11: AZTEC, CODABAR, CODE 39, CODE 128, DATA MATRIX, EAN 8, EAN 13, ITF, PDF 417, QR CODE and UPC A.

Ten formats can be read but not written: CODE 93, DX FILM EDGE, MAXICODE, Telepen, Micro QR Code, rMQR Code, RSS 14, RSS EXPANDED, UPC E and UPC EAN EXTENSION. MAXICODE carries the only qualifier anywhere in either list, marked partial. Both halves describe what the ZXing-C++ library behind the app can do rather than anything Binary Eye contributes, so the asymmetry is inherited from the dependency rather than chosen in the app.

Two smaller inconsistencies sit in the same table. The read entry is spelled PDF417 while the generate entry is PDF 417, with a space, and both point at the same library reference. RSS 14 and RSS EXPANDED share one link target, the GS1 DataBar page, which suits both but leaves the narrower of the two without a reference of its own.

## The third encode example links somewhere other than where it reads

The encoding examples are three lines of markdown, and the third one disagrees with itself. Printed as text it reads content=Test&format=DATA_MATRIX. Its link target carries content=Test2&format=DATA_MATRIX&execute. A reader who copies the printed form sends a different payload from a reader who clicks the written one, and the clickable version also carries the execute argument that the printed text leaves out.

Both forms point at http(s)://markusfisch.de/encode, and neither produces a barcode in the browser. That page is a launcher for the installed app, not a web encoder. Adding execute, or execute=true, is what makes the app produce the code right away instead of waiting for a tap. The sibling deep link binaryeye://encode takes the same content and format arguments with no page in between.

For someone copying the printed form the difference is small: DATA_MATRIX shows up one tap later and the preset text differs by one character. It is still the kind of drift that survives in a file nobody re-reads, and it sits in the one document that explains the whole encoding feature.

## One make target opens a rival scanner's store listing

The Makefile holds a family of targets that push a URI at an attached device through adb, and one of them does not exercise Binary Eye at all.

```make
testxiaomi:
	adb shell am start -a android.intent.action.VIEW \
		-c android.intent.category.BROWSABLE \
		-d 'market://details?id=com.xiaomi.scanner'
```

com.xiaomi.scanner is a different barcode scanner, so the recipe opens another app's store page from the repository of this one. It sits among targets named testview, testscan, testencode and testurl, where the naming implies everything else drives the app under test. It is also the only one of the group with no ampersand to escape and no return URL, which suits a throwaway check rather than a maintained script. Nothing in the target name tells a reader the recipe points at a competitor.

The rest of the group is more careful. Each passes the same intent action, declares the BROWSABLE category, and reaches the app through one of its two documented entry points, which makes the file a working inventory of the deep link surface even where the values are wrong.

## One target builds, installs and launches in a single step

The Makefile opens with one variable and then chains the build straight onto the device:

```make
PACKAGE = de.markusfisch.android.binaryeye

all: debug install start

install:
	adb $(TARGET) install -r app/build/outputs/apk/debug/app-debug.apk

start:
	adb $(TARGET) shell 'am start -n \
		$(PACKAGE).debug/$(PACKAGE).activity.SplashActivity'
```

all runs debug, install and start in sequence, so one make invocation assembles a debug APK, pushes it with adb install -r, and launches the splash activity in a debug-flavoured package. release and bundle both depend on lint test before assembling, so shipped builds have been through lintDebug and test while the default debug path skips both checks.

The infer target depends on clean and then runs infer -- ./gradlew assembleDebug, a separate static analysis tool that has to be on the path before it does anything. No .PHONY line appears in the file, so a stray file named after a target would satisfy make instead of running its recipe.

The root is a standard Android project plus a few extras: .gitignore, CHANGELOG.md, CONTRIBUTING.md, LICENSE, PRIVACY.md, SECURITY.md, app/, build.gradle, fastlane/, gradle.properties, gradle/, gradlew, gradlew.bat, settings.gradle and svg/. No prebuilt binary is committed, the Download heading offers only an F-Droid package page and a Google Play listing, and the Screenshots heading above it is followed immediately by Download, so the images live on the store pages.

## Escaped ampersands reach the device with the backslash still attached

Four of the adb targets escape the ampersand in a deep link as a backslash followed by an ampersand, inside a single-quoted argument:

```make
testencode:
	adb shell am start -a android.intent.action.VIEW \
		-c android.intent.category.BROWSABLE \
		-d 'binaryeye://encode?content=Test\&format=QR_CODE'
```

Inside shell single quotes a backslash is an ordinary character. Nothing strips it, so the URI handed to adb carries a literal backslash in front of the separator. Format still parses, because the query splits on the ampersand either way, but the content value ends in a backslash, and the README does not say whether the app trims it before presetting the text.

The two targets that pass a web URL, testencodeurl and testurl, escape the ampersand the same way. testscan, whose return template is already percent-encoded and therefore has no bare ampersand, leaves its URI alone. The pattern suggests the author was writing for a shell where an unquoted & would background the command, and the single quotes had already settled that.

## The return template differs between the web example and the make target

The documented way to receive a scan is one query argument, ret, holding a URL encoded template:

```text
http://markusfisch.de/BinaryEye?ret=http%3A%2F%2Fexample.com%2F%3Fresult%3D{RESULT}
```

Three symbols get substituted into it: RESULT for the scanned content, RESULT_BYTES for the raw result as a hex string, and FORMAT for the barcode format. The receiving endpoint picks which one it needs, and having the raw bytes available at all is more than most scanners in this category hand over.

The make target that exercises the same path sends the callback to the app's own domain and puts a slash where the web example does not:

```make
testscan:
	adb shell am start -a android.intent.action.VIEW \
		-c android.intent.category.BROWSABLE \
		-d 'binaryeye://scan/?ret=http%3A%2F%2Fmarkusfisch.de%2F%3Fresult%3D{RESULT}'
```

binaryeye://scan/?ret=... rather than binaryeye://scan?ret=.... The braces around RESULT reach the device unencoded, which is what the substitution wants, so that part is deliberate. The extra slash is the one difference between the two, and whether it changes anything depends on how the app splits the deep link path from its query.

## Bluetooth forwarding runs on two receivers maintained elsewhere

Binary Eye can push scans to a computer over Bluetooth, and the receivers on the other end are not part of this repository. The README says so outright: the companion apps are not developed nor maintained by the author of Binary Eye. What ships is a setting named Forward scans with Bluetooth and three steps. Pair the phone and the PC, download and run a companion app, then select the target host device in the app settings.

Two companion projects are named, one per platform: https://github.com/KamaleiZestri/BinaryReceptor for Windows and https://github.com/sean666888/bin_eye_bt_receiver for Linux. No macOS receiver is listed, so a Mac has no documented path for this feature even though the phone side is identical everywhere.

Pairing is not a formality. The host is chosen from the paired devices inside the app, so the phone must already have accepted the computer before the setting will offer it. Anyone using the feature installs a third-party binary from a different repository and points every future scan at it, on the strength of a one-line disclaimer and no version pin.

## Deep links hand over three values, the Intent sample reads one

Two integration surfaces exist, and they expose different amounts of the scan. The deep link contract is three substitution symbols wide: RESULT, RESULT_BYTES and FORMAT. The Intent contract, inherited from the ZXing client convention, is one string extra called SCAN_RESULT:

```kotlin
startActivityForResult(
	Intent("com.google.zxing.client.android.SCAN"),
	SOME_NUMBER
)
```

The AndroidX form in the README registers the same action through registerForActivityResult and reads SCAN_RESULT out of the result. Those samples show only that one extra, and the FORMAT value that the deep link offers has no documented Intent counterpart here, so an app integrating through the Intent should not assume it can ask the scanner which symbology produced a result.

The action string is what makes this app substitutable for any ZXing-era scanner: the intent names a convention, not a package. For developers who want capture inside their own app, the README's integration note points away from this repository entirely, to BarcodeScannerView, also by the same author, or to Google's ML Kit barcode scanning documentation.

## Conclusion

Binary Eye earns a slot on a phone that needs to pass barcodes to other apps, and the deep link contract with RESULT, RESULT_BYTES and FORMAT is the part worth relying on. Treat the make targets as a developer's notebook rather than as documentation: one opens a competing scanner's store page, four pass a character the shell was never going to remove, and the third encode example links somewhere other than where it reads. Before depending on it, install the F-Droid or Play build and check it against your own Android version, since the repository ships no binary and its last commit is dated 2026-10-01.

## FAQ

### what is binary eye

Binary Eye is a free, ad-free, MIT-licensed Android barcode scanner written in Kotlin and built on the ZXing-C++ library. It reads 21 barcode formats and generates 11, works in portrait and landscape, reads inverted codes, and installs from F-Droid or Google Play under the package name de.markusfisch.android.binaryeye.

### Does Binary Eye have an iOS app or work in a browser?

No. The project builds an Android app in Kotlin, the package is de.markusfisch.android.binaryeye, and the two download links are an F-Droid package page and a Google Play listing. The pages at markusfisch.de/BinaryEye and markusfisch.de/encode are launchers that hand the URI to the installed Android app, not a browser-based scanner.

### How do I get a scan into another Android app?

Send the com.google.zxing.client.android.SCAN intent to it and read the SCAN_RESULT string extra from the result, either through startActivityForResult or through the AndroidX registerForActivityResult launcher. To embed capture in your own app instead, the project points at BarcodeScannerView and Google's ML Kit barcode scanning documentation.

### Can Binary Eye forward scans to my computer?

Over Bluetooth, yes, but the receiving side is a separate app: BinaryReceptor on Windows and bin_eye_bt_receiver on Linux. The Binary Eye author does not develop or maintain either one, and no macOS receiver is named. You pair the two devices first, then pick the host inside the Forward scans with Bluetooth setting.

### Can I build the app from the repository?

The root carries gradlew, gradlew.bat, build.gradle, settings.gradle, gradle.properties, an app/ module, fastlane/ and svg/. The Makefile wraps the common steps: all runs debug, install and start, debug runs ./gradlew assembleDebug, and install pushes app/build/outputs/apk/debug/app-debug.apk with adb. No prebuilt binary is committed, so the install and start steps need a device attached.

## Sources

- [License: MIT](https://github.com/markusfisch/BinaryEye/blob/master/LICENSE)
- [markusfisch/BinaryEye on GitHub](https://github.com/markusfisch/BinaryEye)
- [Project website](https://play.google.com/store/apps/details?id=de.markusfisch.android.binaryeye)
- [README](https://github.com/markusfisch/BinaryEye/blob/master/README.md)
- [Releases](https://github.com/markusfisch/BinaryEye/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/markusfisch-binaryeye
