Documentation

Barcode Scanner for Android

Scan QR codes and barcodes from Unity on Android with the Google code scanner: 13 formats, parsed Wi-Fi, contact, link and event data, and no camera permission.

  • Version 2.0.0
  • Unity 6000.0+
  • Platforms Android
  • Updated Nov 4, 2026
Barcode Scanner for Android

Overview

Barcode Scanner for Android opens Google's code scanner from Unity and returns the result to your C# code. Google Play services shows the camera screen, finds the barcode and parses its content, so your application needs no camera permission and no camera code. One call starts a scan, and C# events bring back the result on Unity's main thread.

It reads 13 barcode formats, from QR codes and Data Matrix to EAN-13 and Code 128, and turns structured QR content into typed objects: Wi-Fi networks, contacts, links, calendar events, email, SMS, phone numbers, locations and driver licenses.

Requirements

ItemVersionNotes
Unity6000.0+Android Build Support module
AndroidAPI 21+Google's scanner library needs API 21; your Unity version may require a higher minimum
Google Play servicesOn the deviceThe scanner screen and decoder live in Play services. Devices without it report an error
External Dependency Manager (com.unity.external-dependency-manager)2.0+Unity Registry package. Resolves Google's libraries from Editor/AndroidDependencies.xml
play-services-code-scanner16.1.0Resolved by the External Dependency Manager
play-services-base18.11.1Resolved by the External Dependency Manager; provides the module download API

The plugin works on Android only. In the Editor and on other platforms every call reports UnsupportedPlatformError instead of failing, so the same code compiles everywhere.

Quick start

  1. Import the package Everything lands in Assets/hardartcore/BarcodeScanner.
  2. Add the External Dependency Manager If your project doesn't have it yet, open Window › Package Manager, click +, choose Install package by name and enter com.unity.external-dependency-manager. Switch the build target to Android and run Assets › External Dependency Manager › Android Resolver › Resolve.
  3. Open the demo Open Demo/Scenes/DemoScene, add it to the build, and press File › Build And Run with an Android device connected.
  4. Scan Tap Scan and point the camera at a barcode or QR code. The card shows the type, the format and every parsed field.
  5. Use it in your game Subscribe to the events and call BarcodeScanner.Scan() (Scanning). A prefab is optional.
Tip

The first scan on a device may download Google's scanner module, which needs a connection. Call BarcodeScanner.Initialize() early, for example on your main menu, so the module is ready before the player taps Scan.

Already using EDM4U?

Keep one dependency manager. If the project also has Google's EDM4U, Unity asks which one to use: choose Use Unity's External Dependency Manager. The plugin's AndroidDependencies.xml works with both.

The demo scene

Demo/Scenes/DemoScene is a complete scanner screen built with uGUI. Its script, Demo/Scripts/DemoScript.cs, shows every part of the API:

Status chip
Follows every event: preparing, ready, scanner downloaded, scanning, scanned, cancelled and errors with their code.
Format chips
All formats, QR codes and Product codes (EAN-13, EAN-8, UPC-A, UPC-E) call SetFormats.
Result card
The type with its icon, the format, and each parsed field as a row. Long results scroll. Copy raw value puts the raw value on the clipboard.
Scan button
Calls Scan(), and is disabled while IsScanning is true.

In the Editor the status chip shows Scan on an Android device: scanning needs Google Play services. The scene uses the Standalone Input Module; in a project that uses only the Input System, select the EventSystem and click Replace with InputSystemUIInputModule.

How it works

BarcodeScanner.Scan()                    C#, Unity's main thread
  └─ com.hardartcore.scanner.BarcodeScanner   Java source plugin, compiled by Unity
       └─ Google code scanner                 Google Play services: camera screen, decoder, parser
            └─ result, cancel or error
  ← BarcodeScanListener (AndroidJavaProxy)    Android UI thread
  ← queued, then raised from Update           Unity's main thread: your event handlers

The adapter ships as two Java files in Plugins/Android, which Unity compiles into the Android build; no separate Android library is involved. Google's own libraries are resolved by Unity's External Dependency Manager package.

No camera permission
The camera screen belongs to Google Play services, so the plugin adds no CAMERA permission to your manifest and needs no runtime permission prompt.
Scanner module
The scanning code is a Play services module. If it isn't on the device yet, the plugin requests it and waits for the download, then raises OnBarcodeModuleInstalled and OnModuleAvailable. A scan requested in the meantime starts when the module is ready.
Main thread
Android calls back on its UI thread. The plugin queues every callback and raises the events from Update, so handlers can change UI, scenes and game state directly.
One scan at a time
A second Scan() while a scan is open reports ScanInProgressError and leaves the open scan alone.

Scanning

Subscribe to the events, then call Scan(). All calls belong on Unity's main thread.

ScanExample.cs
using HardArtcore;
using UnityEngine;

public class ScanExample : MonoBehaviour
{
    private void OnEnable()
    {
        BarcodeScanner.OnBarcodeScanned += OnBarcodeScanned;
        BarcodeScanner.OnScanCancelled += OnScanCancelled;
        BarcodeScanner.OnErrorOccurred += OnErrorOccurred;
    }

    private void OnDisable()
    {
        BarcodeScanner.OnBarcodeScanned -= OnBarcodeScanned;
        BarcodeScanner.OnScanCancelled -= OnScanCancelled;
        BarcodeScanner.OnErrorOccurred -= OnErrorOccurred;
    }

    // Call from a button.
    public void Scan() => BarcodeScanner.Scan();

    private void OnBarcodeScanned(Barcode barcode)
    {
        Debug.Log($"{barcode.GetFormat()} {barcode.GetValueType()}: {barcode.GetDisplayValue()}");
    }

    private void OnScanCancelled()
    {
        // The player closed the scanner screen.
    }

    private void OnErrorOccurred(string message, int errorCode)
    {
        // Show the message, offer a retry. See Error codes.
    }
}

Preparing the scanner

The service starts by itself on the first call. Initialize() starts it ahead of time and downloads the scanner module if the device needs it; calling it again does nothing once the module is ready.

MainMenu.cs
private void Start()
{
    BarcodeScanner.OnModuleAvailable += () => scanButton.interactable = true;
    BarcodeScanner.Initialize();
}

Choosing formats

By default the scanner detects every format. Limiting it to the formats you expect makes detection faster and avoids reading the wrong code. Values are copied; null or an empty array resets to all formats. The setting applies to the next scan.

Formats
// QR codes only.
BarcodeScanner.SetFormats(new[] { (int)Barcode.BarcodeFormat.QrCode });

// Retail product codes.
BarcodeScanner.SetFormats(new[]
{
    (int)Barcode.BarcodeFormat.Ean13, (int)Barcode.BarcodeFormat.Ean8,
    (int)Barcode.BarcodeFormat.UpcA, (int)Barcode.BarcodeFormat.UpcE,
});

// Everything again.
BarcodeScanner.SetFormats(null);

An unknown or negative value throws ArgumentOutOfRangeException before anything reaches Android.

BarcodeScanner reference

Everything is static on HardArtcore.BarcodeScanner.

Methods and properties

Scan()
Opens the scanner screen. Prepares the scanner module first if needed. Ends with exactly one of OnBarcodeScanned, OnScanCancelled or OnErrorOccurred.
Initialize()
Optional. Starts the service and prepares the scanner module ahead of the first scan.
SetFormats(int[] formats)
Formats to detect, as Barcode.BarcodeFormat values. null or empty: all formats.
IsSupported
true on Android. It does not check whether Google Play services is installed.
IsReady
true once the scanner module is available.
IsScanning
true from Scan() until its result, including the time spent waiting for a module download.

Events

All events are raised on Unity's main thread.

OnBarcodeScanned (Barcode)
A barcode was read. See Reading results.
OnScanCancelled
The player closed the scanner screen without scanning.
OnErrorOccurred (string message, int errorCode)
Preparation or the scan failed. Both can be retried.
OnModuleAvailable
The scanner module is ready, whether it was already on the device or just downloaded.
OnBarcodeModuleInstalled
The module was just downloaded. Followed by OnModuleAvailable.

Error codes

ConstantValueMeaning
UnsupportedPlatformError-1001Not running on Android (the Editor, iOS, desktop...)
ScanInProgressError-1002A scan is already open
InitializationError-1003The service, the module or the activity was not available
ModuleInstallCancelledError-1005The scanner module download was cancelled; scanning again retries
InvalidFormatError-1006The Android side received an unsupported format value
Other values> 0Passed through from Google Play services (MlKitException and ApiException codes)

Reading results

OnBarcodeScanned passes a Barcode. Every barcode has a format, a value type, a raw value and a display value. Structured content has a typed getter that matches its value type; the others return null.

Typed results
private void OnBarcodeScanned(Barcode barcode)
{
    switch (barcode.GetValueType())
    {
        case Barcode.BarcodeType.URL:
            Application.OpenURL(barcode.GetUrl().GetUrl());
            break;
        case Barcode.BarcodeType.Wifi:
            var wifi = barcode.GetWifi();
            Connect(wifi.GetSsid(), wifi.GetPassword(), wifi.GetEncryptionType());
            break;
        case Barcode.BarcodeType.Product:
            LookUpProduct(barcode.GetRawValue()); // e.g. 5901234123457
            break;
        default:
            ShowText(barcode.GetDisplayValue());
            break;
    }
}

Barcode

GetFormat()
The symbology, a Barcode.BarcodeFormat.
GetValueType()
What the content is, a Barcode.BarcodeType.
GetRawValue()
The encoded text, unchanged. May be null for binary content.
GetDisplayValue()
A readable form of the value, for example the URL of a bookmark.

Value types

BarcodeTypeGetterFields
URLGetUrl() → UrlBookmarkGetTitle, GetUrl
WifiGetWifi() → WiFiGetSsid, GetPassword, GetEncryptionType (Open, Wpa, Wep)
ContactInfoGetContactInfo() → ContactInfoGetName (PersonName), GetOrganization, GetTitle, GetPhones, GetEmails, GetAddresses, GetUrls
EmailGetEmail() → EmailGetAddress, GetSubject, GetBody, GetEmailType
PhoneGetPhone() → PhoneGetNumber, GetPhoneType (Work, Home, Fax, Mobile)
SmsGetSms() → SmsGetPhoneNumber, GetMessage
GeoGetGeoPoint() → GeoPointGetLatitude, GetLongitude
CalendarEventGetCalendarEvent() → CalendarEventGetSummary, GetStart/GetEnd (CalendarDateTime), GetLocation, GetOrganizer, GetDescription, GetStatus
DriverLicenseGetDriverLicense() → DriverLicenseNames, GetLicenseNumber, birth, issue and expiry dates, issuing country, address, gender, document type (AAMVA)
Isbn, Product, Text, UnknownnoneRead GetRawValue() or GetDisplayValue()

PersonName has first, middle, last, prefix, suffix, pronunciation and formatted name. Address has GetAddressType and GetAddressLines. CalendarDateTime has year, month, day, hours, minutes, seconds and the raw value. Every class has a readable ToString() for debugging.

Note

Results read their values from the Android object on each call. Read what you need in the event handler, and keep plain strings or numbers rather than the Barcode itself.

Formats

BarcodeFormatValueKind
Code1281Linear
Code392Linear
Code934Linear
Codabar8Linear
DataMatrix162D
Ean1332Linear (product)
Ean864Linear (product)
Itf128Linear
QrCode2562D
UpcA512Linear (product)
UpcE1024Linear (product)
Pdf41720482D
Aztec40962D

AllFormats (0) and Unknown (-1) complete the enum. The values match Google's constants. Google's scanner does not read 1D barcodes with one character, ITF codes shorter than six characters, codes using FNC2, FNC3 or FNC4, or QR codes in ECI mode.

Lifecycle

No prefab needed
The first call creates a BarcodeScanner GameObject. You can also place Prefabs/BarcodeScanner.prefab at the root of your first scene so it starts preparing at once.
One instance
The service persists between scenes (DontDestroyOnLoad). A second BarcodeScanner component, for example from the prefab in another scene, removes itself.
Cleanup
Destroying the component releases the Android service and its listeners. The scanner screen itself belongs to Google Play services and closes on its own.
Domain reload
Static events are cleared when Play mode starts, so it works with Enter Play Mode Options and domain reload turned off.
Privacy
The plugin never logs scanned content. Results go only to your event handlers.

Android build

  1. Resolve the dependencies The External Dependency Manager reads Editor/AndroidDependencies.xml. With Patch mainTemplate.gradle turned on, Gradle downloads the libraries during the build; otherwise the resolver copies them into Assets/Plugins/Android. Both work.
  2. Build Unity compiles BarcodeScanner.java and BarcodeScanListener.java into the Gradle project. Keep both enabled for Android only in the Plugin Inspector (they ship that way).
  3. Release builds Editor/BarcodeScannerAndroidBuild.cs adds R8 keep rules for the adapter and for Google's result classes, which are read by name from C#, so minification keeps them. No setup needed.
Upgrading from 1.x

Delete Plugins/Android/scanner-release.aar. Importing a package never removes old files, and the AAR defines the same Java classes as the new sources. The build stops with its path until it is gone.

The adapter's managed callbacks carry [Preserve], so they survive IL2CPP code stripping. Test a release build on a device before you publish, as with any native plugin.

Folder layout

Assets/hardartcore/BarcodeScanner/
├── Documentation.html            this page, offline
├── Scripts/
│   ├── BarcodeScanner.cs         the API: Scan, Initialize, SetFormats, events
│   └── Data/                     Barcode and its typed results (UrlBookmark, WiFi, ContactInfo...)
├── Plugins/Android/
│   ├── BarcodeScanner.java       the Android adapter, compiled by Unity
│   └── BarcodeScanListener.java
├── Editor/
│   ├── AndroidDependencies.xml   Google libraries for the External Dependency Manager
│   └── BarcodeScannerAndroidBuild.cs   R8 rules, legacy AAR check
├── Prefabs/BarcodeScanner.prefab optional
└── Demo/
    ├── Scenes/DemoScene.unity
    ├── Scripts/DemoScript.cs
    ├── Sprites/                  rounded shape, backdrop, Material Symbols icons
    └── Fonts/                    Nunito

Delete Demo if you don't need it; nothing else depends on it.

Troubleshooting

Every call reports -1001
You are in the Editor or on another platform. Build to an Android device.
Gradle: duplicate class com.hardartcore.scanner...
The 1.x scanner-release.aar is still in the project. Delete it.
Gradle: package com.google.mlkit... does not exist
The Google libraries were not resolved. Run Assets › External Dependency Manager › Android Resolver › Force Resolve.
Unity asks which dependency manager to use
The project also has Google's EDM4U, for example in Assets/ExternalDependencyManager. Choose Use Unity's External Dependency Manager, and remove the old .unitypackage install unless another plugin strictly needs it. Keep only one dependency manager active; you can switch later in Assets › External Dependency Manager.
The first scan waits, or fails offline
The scanner module is downloading. It needs a connection once; call Initialize() early.
An error with a positive code
It comes from Google Play services: often Play services is missing, outdated or disabled on the device.
Buttons don't respond in the demo
The project uses only the Input System: replace the EventSystem's module with InputSystemUIInputModule.
NoSuchMethodError in a release build
Another build script replaced unityLibrary/build.gradle. Check that the line with hardartcore-barcode-scanner.pro is present.

Third-party notices

The demo uses the Nunito font (SIL Open Font License 1.1) and Material Symbols icons (Apache License 2.0). Google's Play services libraries are not included in the package: the External Dependency Manager downloads them under Google's terms.

The full licence texts ship in the package's Documentation.html, and online: SIL Open Font License 1.1, Apache License 2.0.

Release notes

2.0.0

  • The Android adapter ships as Java source compiled by Unity; the separate scanner-release.aar is gone. Delete it when upgrading (the build tells you if it is still there).
  • Events are raised on Unity's main thread. Before, handlers ran on Android's UI thread.
  • A newly downloaded scanner module now raises OnBarcodeModuleInstalled when the download finishes, then OnModuleAvailable.
  • Fixed format values: Code128 is 1 and Code39 is 2, and Code93 (4) was added. Update any stored numeric values.
  • New: Initialize(), IsSupported, IsReady, IsScanning and error constants. A prefab is optional; one instance persists between scenes.
  • SetFormats copies its array, resets on null or empty, and throws on unknown values.
  • A second scan while one is open reports ScanInProgressError. Unsupported platforms report UnsupportedPlatformError.
  • R8 keep rules for release builds, [Preserve] on the callbacks, and no logging of scanned content.
  • Google libraries updated to play-services-code-scanner 16.1.0 and play-services-base 18.11.1.
  • Uses Unity's External Dependency Manager package (com.unity.external-dependency-manager) from the Unity Registry.
  • A new demo screen: status, format filters, and a result card with every parsed field. It no longer needs TextMesh Pro.
  • Requires Unity 6.

1.0.0

  • First release.

Need help?

Questions, bug reports or feature requests: I usually reply within a day.

support@hardartcore.com

Last updated November 4, 2026.