Android Example Using Camera2 with Zxing for live scan of 1D and 2D barcodes

I have used @Erel's sample project from here:


Have added a timer to get the preview bitmap every 300ms while the preview is active. Then used a bit of inline Java to decode the barcode image (1D and 2D) from preview frame. Preview frame is displayed in the imageview at the right top of the display. Also added a label to show the "text" extracted from the barcode(s).

Just start the camera and point it to a barcode. No need to take a picture.

Note this in the code:
B4X:
#AdditionalJar: core-3.3.2.jar

Change it to your liking.....
It will decode:
UPC_A
UPC_E
EAN_13
EAN_8
CODABAR
CODE_39
CODE_93
CODE_128
ITF
RSS_14
RSS_EXPANDED
QR_CODE
DATA_MATRIX
AZTEC
PDF417
MAXICODE

Extract the jar from core-3.3.2.zip and copy the jar to your additional libs folder.
B4A sample project attached
2.png



1.png


Sample code (Main):
B4X:
#Region  Project Attributes
    #ApplicationLabel: Camera2 Example
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#AdditionalJar: core-3.3.2.jar

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: False
#End Region
#BridgeLogger: true

Sub Process_Globals
    Private frontCamera As Boolean = False
    Private VideoMode As Boolean = False
    Private VideoFileDir, VideoFileName As String
    Private MyTaskIndex As Int
    Private rp As RuntimePermissions
   
    Dim nativeMe As JavaObject
   
    Dim t As Timer
   
End Sub

Sub Globals
    Private cam As CamEx2
    Private pnlCamera As Panel
    Private pnlPicture As Panel
    Private pnlBackground As Panel
    Private btnEffects As Button
    Private btnScene As Button
    Private buttons As List
    Private btnAutoExposure As Button
    Private btnFocus As Button
    Private ProgressBar1 As ProgressBar
    Private openstate, busystate As Boolean
    Private btnRecord As Button
    Private btnMode As Button
    Private btnCamera As Button
    Private barZoom As SeekBar
    Private ImageView1 As ImageView
    Private Label1 As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
    nativeMe.InitializeContext
    t.Initialize("t", 300)
    VideoFileDir = rp.GetSafeDirDefaultExternal("")
    VideoFileName = "1.mp4"
    Activity.LoadLayout("1")
    Activity.LoadLayout("StillPicture")
    cam.Initialize(pnlCamera)
    Log(cam.SupportedHardwareLevel)
    buttons = Array(btnScene, btnAutoExposure, btnEffects, btnFocus, btnMode)
    SetState(False, False, VideoMode)
End Sub

Sub Activity_Resume
    OpenCamera(frontCamera)
End Sub


Sub OpenCamera (front As Boolean)
    rp.CheckAndRequest(rp.PERMISSION_CAMERA)
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
    If Result = False Then
        ToastMessageShow("No permission!", True)
        Return
    End If
   
    SetState(False, False, VideoMode)
    Wait For (cam.OpenCamera(front)) Complete (TaskIndex As Int)
    If TaskIndex > 0 Then
        MyTaskIndex = TaskIndex 'hold this index. It will be required in later calls.
        Wait For(PrepareSurface) Complete (Success As Boolean)
        t.Enabled = True
    End If
    Log("Start success: " & Success)
    SetState(Success, False, VideoMode)
    If Success = False Then
        ToastMessageShow("Failed to open camera", True)
    End If
End Sub

Sub PrepareSurface As ResumableSub
    SetState(False, busystate, VideoMode)
    'sizes can be modified here
    If VideoMode Then
        cam.PreviewSize.Initialize(640, 480)
        'Using a temporary file to store the video.
        Wait For (cam.PrepareSurfaceForVideo(MyTaskIndex, VideoFileDir, "temp-" & VideoFileName)) Complete (Success As Boolean)
    Else
        cam.PreviewSize.Initialize(1920, 1080)
        Wait For (cam.PrepareSurface(MyTaskIndex)) Complete (Success As Boolean)
    End If
    If Success Then cam.StartPreview(MyTaskIndex, VideoMode)
    SetState(Success, busystate, VideoMode)
    Return Success
End Sub

Sub btnCamera_Click
    frontCamera = Not(frontCamera)
    OpenCamera(frontCamera)
End Sub

Sub Activity_Pause (UserClosed As Boolean)
    t.Enabled = False
    cam.Stop
   
End Sub

Sub btnMode_Click
    VideoMode = Not(VideoMode)
    If VideoMode Then
        rp.CheckAndRequest(rp.PERMISSION_RECORD_AUDIO)
        Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
        If Result = False Then
            ToastMessageShow("No permission!", True)
            VideoMode = False
        End If
    End If
    SetState(openstate, busystate, VideoMode)
    PrepareSurface
End Sub

Sub btnRecord_Click
    If VideoMode Then
        CaptureVideo
    Else
        TakePicture
    End If
End Sub

Sub CaptureVideo
    Try
        SetState(openstate, True, VideoMode)
        If cam.RecordingVideo = False Then
            cam.StartVideoRecording (MyTaskIndex)
        Else
            cam.StopVideoRecording (MyTaskIndex)
            File.Copy(VideoFileDir, "temp-" & VideoFileName, VideoFileDir, VideoFileName)
            ToastMessageShow($"Video file saved: $1.2{File.Size(VideoFileDir, VideoFileName) / 1024 / 1024} MB"$, True)
            Wait For (PrepareSurface) Complete (Success As Boolean)
            SetState(openstate, False, VideoMode)
           
        End If
    Catch
        HandleError(LastException)
    End Try
End Sub

Sub HandleError (Error As Exception)
    Log("Error: " & Error)
    ToastMessageShow(Error, True)
    OpenCamera(frontCamera)
End Sub

Sub TakePicture
    Try
        SetState(openstate, True, VideoMode)
        Wait For(cam.FocusAndTakePicture(MyTaskIndex)) Complete (Data() As Byte)
        SetState(openstate, False, VideoMode)
        cam.DataToFile(Data, VideoFileDir, "1.jpg")
        Dim bmp As Bitmap = cam.DataToBitmap(Data)
        Log("Picture taken: " & bmp) 'ignore
        pnlBackground.SetVisibleAnimated(100, True)
        pnlPicture.SetBackgroundImage(bmp.Resize(pnlPicture.Width, pnlPicture.Height, True)).Gravity = Gravity.CENTER
        Sleep(4000)
        pnlBackground.SetVisibleAnimated(500, False)
    Catch
        HandleError(LastException)
    End Try
   
End Sub

Sub pnlPicture_Click
    pnlBackground.Visible = False
End Sub

Sub btnEffects_Click
    Dim effects As List = cam.SupportedEffectModes
    Dim i As Int = effects.IndexOf(cam.EffectMode)
    i = (i + 1) Mod effects.Size
    cam.EffectMode = effects.Get(i)
    btnEffects.Text = effects.Get(i)
    cam.StartPreview(MyTaskIndex, VideoMode)
End Sub

Sub btnScene_Click
    Dim scenes As List = cam.SupportedSceneModes
    Dim i As Int = scenes.IndexOf(cam.SceneMode)
    i = (i + 1) Mod scenes.Size
    cam.SceneMode = scenes.Get(i)
    btnScene.Text = scenes.Get(i)
    cam.StartPreview(MyTaskIndex, VideoMode)
End Sub

Sub btnAutoExposure_Click
    Dim flashes As List = cam.SupportedAutoExposureModes
    Dim i As Int = flashes.IndexOf(cam.AutoExposureMode)
    i = (i + 1) Mod flashes.Size
    cam.AutoExposureMode = flashes.Get(i)
    btnAutoExposure.Text = flashes.Get(i)
    cam.StartPreview(MyTaskIndex, VideoMode)
End Sub

Sub btnFocus_Click
    Dim focuses As List = cam.SupportedFocusModes
    Dim i As Int = focuses.IndexOf(cam.FocusMode)
    i = (i + 1) Mod focuses.Size
    cam.FocusMode = focuses.Get(i)
    btnFocus.Text = focuses.Get(i)
    cam.StartPreview(MyTaskIndex, VideoMode)
End Sub

'This sub enables or disables the various UI elements based on the current state.
Sub SetState(Open As Boolean, Busy As Boolean, Video As Boolean)
    For Each b As Button In buttons
        b.Visible = Open And Not(Busy)
    Next
    btnCamera.Visible = Not(Busy)
    btnRecord.Visible = Open And (Video Or Not(Busy))
    openstate = Open
    ProgressBar1.Visible = Busy
    busystate = Busy
    VideoMode = Video
    barZoom.Visible = Open
    Dim btnRecordText As String
    If VideoMode Then
        If Busy Then
            btnRecordText = Chr(0xF04D)
        Else
            btnRecordText = Chr(0xF03D)
        End If
    Else
        btnRecordText = Chr(0xF030)
    End If
    btnRecord.Text = btnRecordText
End Sub

Sub barZoom_ValueChanged (Value As Int, UserChanged As Boolean)
    Dim OriginalSize As Rect = cam.ActiveArraySize
    Dim Zoom As Float = 1 + Value / 100 * (cam.MaxDigitalZoom - 1)
    Dim Crop As Rect
    Dim NewWidth As Int = OriginalSize.Width / Zoom
    Dim NewHeight As Int = OriginalSize.Height / Zoom
    Crop.Initialize(OriginalSize.CenterX - NewWidth / 2, OriginalSize.CenterY - NewHeight / 2, _
        OriginalSize.CenterX + NewWidth / 2, OriginalSize.CenterY + NewHeight / 2)
    cam.PreviewCropRegion = Crop
    cam.StartPreview(MyTaskIndex, VideoMode)
End Sub

Sub t_tick
   
    Dim mbm As Bitmap = cam.GetPreviewBitmap(480, 640)
    ImageView1.Bitmap = mbm
    Dim s As String = nativeMe.RunMethod("decodeQRImage", Array(mbm))
    Label1.Text = s
    Log("S = " & s)
   
   
End Sub

#if Java

import com.google.zxing.MultiFormatWriter;
import android.graphics.Bitmap;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.qrcode.decoder.Version;

import android.graphics.BitmapFactory;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.LuminanceSource;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.MultiFormatReader;


import android.util.Base64;
import java.io.ByteArrayOutputStream;


public String decodeQRImage(Bitmap bMap) {

    String decoded = "";

    int[] intArray = new int[bMap.getWidth() * bMap.getHeight()];
    bMap.getPixels(intArray, 0, bMap.getWidth(), 0, 0, bMap.getWidth(),
            bMap.getHeight());
    LuminanceSource source = new RGBLuminanceSource(bMap.getWidth(),
            bMap.getHeight(), intArray);
    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

   
    Reader reader = new MultiFormatReader();
    try {
   
        Result result = reader.decode(bitmap);
        decoded = result.getText();
        BA.Log("HERE");
       

    } catch (NotFoundException e) {
         BA.Log("NotFoundException: " + e.getMessage());
    } catch (ChecksumException e) {
        BA.Log("ChecksumException: " + e.getMessage());
    } catch (FormatException e) {
        BA.Log("FormatException: " + e.getMessage());
    }
    return decoded;
}

#End If
 

Attachments

  • core-3.3.2.zip
    493.6 KB · Views: 1,224
  • Camera2.zip
    16.5 KB · Views: 1,192
Last edited:

Johan Schoeman

Expert
Licensed User
Longtime User
This will show the "text" as well as the type of barcode that was scanned successfully. You still need to download and extract the jar from core-3.3.2.zip in post #1 above and copy the jar to your additional libs folder.



Some of the code:
B4X:
Sub t_tick
   
    Dim mbm As Bitmap = cam.GetPreviewBitmap(480, 640)
    ImageView1.Bitmap = mbm
    Dim s As String = nativeMe.RunMethod("decodeQRImage", Array(mbm))
    Label1.Text = s
    Log("S = " & s)
   
   
End Sub

#if Java

import com.google.zxing.MultiFormatWriter;
import android.graphics.Bitmap;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.qrcode.decoder.Version;

import android.graphics.BitmapFactory;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.LuminanceSource;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.MultiFormatReader;

public Result result;

public String decodeQRImage(Bitmap bMap) {

    String decoded = "";

    int[] intArray = new int[bMap.getWidth() * bMap.getHeight()];
    bMap.getPixels(intArray, 0, bMap.getWidth(), 0, 0, bMap.getWidth(),
            bMap.getHeight());
    LuminanceSource source = new RGBLuminanceSource(bMap.getWidth(),
            bMap.getHeight(), intArray);
    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

    Reader reader = new MultiFormatReader();
    try {
   
        result = reader.decode(bitmap);
        decoded = result.getText();
       
    } catch (NotFoundException e) {
         BA.Log("NotFoundException: " + e.getMessage());
    } catch (ChecksumException e) {
        BA.Log("ChecksumException: " + e.getMessage());
    } catch (FormatException e) {
        BA.Log("FormatException: " + e.getMessage());
    }
    if (decoded != "") {
       return decoded + "\r\n" + result.getBarcodeFormat().toString();
    } else {
       return decoded;  
    }  
}

#end if
3.png


4.png
 

Attachments

  • Camera2_12040202141200.zip
    16.5 KB · Views: 569
Last edited:

Johan Schoeman

Expert
Licensed User
Longtime User
This update will add the resultpoints to the bitmap of the decoded barcode (1D / 2D). Have moved the imageview to (almost) bottom right hand side.

5.png
6.png
7.png
 

Attachments

  • Camera2_1304020083800.zip
    17.2 KB · Views: 704
Last edited:

MARCO C.

Active Member
Licensed User
hi everyone, but is it normal that I can only use the secondary camera ? is there any method to be able to use the primary one?
Thank you
 

Johan Schoeman

Expert
Licensed User
Longtime User
hi everyone, but is it normal that I can only use the secondary camera ? is there any method to be able to use the primary one?
Thank you
Not sure I follow your question? What are you referring to with primary camera? The primary camera is usually the back facing camera. Or are you referring to the native Android camera?
 

Johan Schoeman

Expert
Licensed User
Longtime User
Exact, instead, as I open the app, I do not start the "the back facing camera", but the front one .... the selfie one :)

Grazie


See this in the code (frontCamera is set to false - so it will start the back facing camera. Change the value to True to start the front camera):
B4X:
Sub Process_Globals
    Private frontCamera As Boolean = False

B4X:
Sub OpenCamera (front As Boolean)
    rp.CheckAndRequest(rp.PERMISSION_CAMERA)
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
    If Result = False Then
        ToastMessageShow("No permission!", True)
        Return
    End If
  
    SetState(False, False, VideoMode)
    Wait For (cam.OpenCamera(front)) Complete (TaskIndex As Int)
    If TaskIndex > 0 Then
        MyTaskIndex = TaskIndex 'hold this index. It will be required in later calls.
        Wait For(PrepareSurface) Complete (Success As Boolean)
        t.Enabled = True
    End If
    Log("Start success: " & Success)
    SetState(Success, False, VideoMode)
    If Success = False Then
        ToastMessageShow("Failed to open camera", True)
    End If
End Sub
 

MARCO C.

Active Member
Licensed User
Not sure I follow your question? What are you referring to with primary camera? The primary camera is usually the back facing camera. Or are you referring to the native Android camera?

solved, the smartphone does not allow me to switch the camera. thanks a lot
 

MARCO C.

Active Member
Licensed User
See this in the code (frontCamera is set to false - so it will start the back facing camera. Change the value to True to start the front camera):
B4X:
Sub Process_Globals
    Private frontCamera As Boolean = False

B4X:
Sub OpenCamera (front As Boolean)
    rp.CheckAndRequest(rp.PERMISSION_CAMERA)
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
    If Result = False Then
        ToastMessageShow("No permission!", True)
        Return
    End If

    SetState(False, False, VideoMode)
    Wait For (cam.OpenCamera(front)) Complete (TaskIndex As Int)
    If TaskIndex > 0 Then
        MyTaskIndex = TaskIndex 'hold this index. It will be required in later calls.
        Wait For(PrepareSurface) Complete (Success As Boolean)
        t.Enabled = True
    End If
    Log("Start success: " & Success)
    SetState(Success, False, VideoMode)
    If Success = False Then
        ToastMessageShow("Failed to open camera", True)
    End If
End Sub

Thanks but
I think it's a hardware problem. On my Note 9 everything is ok. On an old Galaxy S4, which I use for testing, he answers me with
"Start success: false"
"NotFoundException: null"

1587288388718.png


Galaxy S4 : Version Android 5.0.1
 

Johan Schoeman

Expert
Licensed User
Longtime User
Thanks but
I think it's a hardware problem. On my Note 9 everything is ok. On an old Galaxy S4, which I use for testing, he answers me with
"Start success: false"
"NotFoundException: null"

View attachment 92074

Galaxy S4 : Version Android 5.0.1
Change the code of OpenCamera(......) to:
B4X:
Sub OpenCamera (front As Boolean)
    rp.CheckAndRequest(rp.PERMISSION_CAMERA)
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
    If Result = False Then
        ToastMessageShow("No permission!", True)
        Return
    End If
   
    SetState(False, False, VideoMode)
    Wait For (cam.OpenCamera(front)) Complete (TaskIndex As Int)
    If TaskIndex > 0 Then
        MyTaskIndex = TaskIndex 'hold this index. It will be required in later calls.
        Wait For(PrepareSurface) Complete (Success As Boolean)
    End If
    Log("Start success: " & Success)
    SetState(Success, False, VideoMode)
    If Success = False Then
        ToastMessageShow("Failed to open camera", True)
    Else
        t.Enabled = True
    End If  
End Sub

It should eliminate the NotFoundException: null error logged by the inline Java code.
The other error (Start success: false) is logged by the B4A code because opening the camera was not successful.
 

MARCO C.

Active Member
Licensed User
negative,
same result.
If I understand correctly, it has moved
t.Enabled = True
in the IF block ??
 

hatzisn

Expert
Licensed User
Longtime User
negative,
same result.
If I understand correctly, it has moved
t.Enabled = True
in the IF block ??

Maybe it is kind of a late response but better late than never.

Take a look at this link:

In the link above you can see that the default Android is 4.2.2 and it is upgradeable to 5.0.1
Api version 4.2.2 is API level 17 and 5.0.1 is API level 21. Camera 2 API can be used from API level 21 so if you have not upgraded your phone's Android to 5.0.1 it is logical not to work.

I hope this helps.
 

Cristian Rufino

Member
Licensed User
Hello everyone:

Is there a way to turn the flashlight on or off manually while trying to scan a code?
The example project works very well but would need to scan codes in low light conditions

Add the following code to the example, the idea is to leave the flashlight on only while touching the screen:

Torch On/Off:
Sub Process_Globals
' ...
' ...
' ...
    Private TorchOn As Boolean = False
End Sub

Private Sub pnlCamera_Touch (Action As Int, X As Float, Y As Float)
    Select Action
        Case 1
            If TorchOn Then
                TorchOn = False
                TurnTorchOnOff(False)
            End If
            
        Case 2
            If Not(TorchOn) Then
                TorchOn = True
                TurnTorchOnOff(True)
            End If
    End Select
End Sub

Private Sub TurnTorchOnOff(On As Boolean)
    Try
        Dim JOCam As JavaObject = cam.Camera
        Dim JOCamManager As JavaObject = JOCam.GetField("cameraManager")
        JOCamManager.RunMethod("setTorchMode", Array(cam.Camera.FindCameraId(False), On))
    Catch
        Log(LastException)
    End Try
End Sub

But the log returns the following error:

(ServiceSpecificException) android.os.ServiceSpecificException: setTorchMode:1628: Torch for camera "0" is not available due to an existing camera user (code 7)
 
Last edited:

Johan Schoeman

Expert
Licensed User
Longtime User
Hello everyone:

Is there a way to turn the flashlight on or off manually while trying to scan a code?
The example project works very well but would need to scan codes in low light conditions
You will have to modify the CamEx2 class and add a button to the activity to control the torch (on/off). The inline Java code cannot be used to switch on/off the torch as the camera is already occupied by the Camera2 application. The inline java code only uses the preview frames from the Camera2 project to decode barcodes should there be any present in the preview frames passed from Carmera2 project to the inline java code.
 

xiaoyao

Active Member
Licensed User
Longtime User
This will show the "text" as well as the type of barcode that was scanned successfully. You still need to download and extract the jar from core-3.3.2.zip in post #1 above and copy the jar to your additional libs folder.

where is ( your additional libs folder.)? thank you
unpacked-core-1.9.0\res\values\values.xml:112: error: resource android:attr/lStar not found.
Originally SDK 28, replaced with SDK 33
Solution: core-33.2.jar needs to be placed in the libraries directory

it's ok,,thank you!
 
Last edited:
Top