-2

I have found some good looking code examples (like this one: http://www.codepool.biz/ocr-barcode-twain/how-to-implement-a-simple-barcode-scan-application-on-android.html) that describe how to use the ZXing library to decode QR codes in an android app.

However, there seem to be classes missing from the ZXing 2.3 core.jar file such as BufferedImageLuminanceSource which is used in most of the code I have found. That being the case, I decided to try to reverse-engineer pieces of the barcode scanner app to fit and came up with the following:

The Result variable rawResult in the onPreviewFrame() method is always null. What am I doing wrong?

EDIT: Integrating ZXing using Intents is impossible for this project, just FYI.

Layout:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:drawable/btn_default"
    tools:context=".RedactedFragment" >

    <SurfaceView
        android:id="@+id/cameraViewfinder"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clickable="true" />

</FrameLayout>

RedactedFragment.java:

import java.util.Map;
import java.util.TreeMap;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;

import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.os.Bundle;
import android.app.Activity;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.widget.Toast;

/**
 * An {@link android.app.Fragment} subclass for the ###{@link RedactedActivity}### which will serve as the fourth {@link Fragment} to be displayed inside the ###{@link RedactedActivity}###.
 */
public class RedactedFragment extends Fragment implements Callback,
        PreviewCallback, OnClickListener, OnLayoutChangeListener {
    FragmentSwapperIFace act;

    Camera cam;
    boolean cameraReady = false;

    SurfaceView cameraViewfinder;
    SurfaceHolder surfaceHolder;

    int wx, hy;

    public RedactedFragment() {
        // Required empty public constructor
    }

    /**
     * The {@link Fragment#onPause()} method is overridden to ensure that the camera is released cleanly by this {@link Fragment}
     * @see android.app.Fragment#onPause()
     */
    @Override
    public void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        cam.setPreviewCallback(null);
        cam.release();
    }

    /**
     * The {@link Fragment#onResume()} method is overridden to ensure that the camera is properly setup and that the {@link PreviewCallback} is added to it.
     * @see android.app.Fragment#onResume()
     */
    @Override
    public void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        cam = Camera.open();
        cam.setPreviewCallback(this);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View v = inflater.inflate(R.layout.fragment_qrscan, container, false);

        cameraViewfinder = (SurfaceView) v.findViewById(R.id.cameraViewfinder);
        surfaceHolder = cameraViewfinder.getHolder();
        surfaceHolder.addCallback(this);
        cameraViewfinder.addOnLayoutChangeListener(this);
        cameraViewfinder.setOnClickListener(this);

        return v;
    }

    /**
     * This method is overridden to add the {@link FragmentSwapperIFace} for requesting that the parent {@link Activity} swap this part of the UI for a different {@link Fragment}
     * @see android.app.Fragment#onAttach(android.app.Activity)
     */
    @Override
    public void onAttach(Activity activity) {
        // TODO Auto-generated method stub
        super.onAttach(activity);
        try {
            act = (FragmentSwapperIFace) activity;
        } catch (Exception e) {

        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        Camera.Parameters parameters = cam.getParameters();
        Camera.Size size = getBestPreviewSize(width, height, parameters);
        if (size != null) {
            parameters.setPreviewSize(size.width, size.height);
            cam.setParameters(parameters);
            cam.startPreview();
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            cam.setPreviewDisplay(holder);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onClick(View v) {
        act.swap(this, new ResultFragment(), R.id.layoutFragment_qr_fragment);
    }

    /**
     * Method borrowed from the ZXing team's barcode scanner app
     * @return {@link Camera.Size}
     */
    private Camera.Size getBestPreviewSize(int width, int height,
            Camera.Parameters parameters) {
        Camera.Size result = null;
        for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
            if (size.width <= width && size.height <= height) {
                if (result == null) {
                    result = size;
                } else {
                    int resultArea = result.width * result.height;
                    int newArea = size.width * size.height;
                    if (newArea > resultArea) {
                        result = size;
                    }
                }
            }
        }
        return (result);
    }

    @Override
    public void onPreviewFrame(final byte[] data, Camera camera) {
        if (!cameraReady)
            return;
        Rect rect = getFramingRect();

        PlanarYUVLuminanceSource src = new PlanarYUVLuminanceSource(data, camera.getParameters().getPictureSize().width, camera.getParameters().getPictureSize().height, rect.left, rect.top, camera.getParameters().getPictureSize().width, camera.getParameters().getPictureSize().height, true);

        Result rawResult = null;
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(src));
        Map<DecodeHintType, String> hints = new TreeMap<DecodeHintType, String>();
        hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
        try {
            rawResult = new QRCodeReader().decode(bitmap);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(rawResult == null){
            Toast.makeText(getActivity(), "rawResult is null", Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(getActivity(), rawResult.getText(), Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * This method determines the size of the {@link SurfaceView} that the camera preview will be displayed on
     * @return {@link Rect} a rectangle of the dimensions of the {@link SurfaceView}
     * @author Original method by the ZXing team
     */
    public Rect getFramingRect() {
        Rect framingRect;
        if (cam == null)
            return null;
        framingRect = new Rect(0, 0, wx, hy);
        return framingRect;
    }

    @Override
    public void onLayoutChange(View v, int left, int top, int right,
            int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        if (v.getId() == cameraViewfinder.getId()) {
            wx = v.getWidth();
            hy = v.getHeight();
            cameraReady = true;
        }
    }
}

UPDATE: I have now copied this fragment into its own test app with the same operating conditions and it is working in the test app. There is plenty of memory available on the test device and the fragment is instantiated in exactly the same way in the test app and the real app. Why is the new QRCodeReader.decode(bitmap) method always returning null in the real app using the same code?

KG6ZVP
  • 3,610
  • 4
  • 26
  • 45
  • Given the fact your fragment works on its own and that is all we can see, I don't think we have enough information to answer your question (possibly why it has been downvoted). The nuclear option is to compile ZXing from source and step through in debug mode to trace where your null is coming from: https://github.com/zxing/zxing – Rupert Rawnsley Feb 26 '14 at 08:14
  • have a look [here][1] it will help you [1]: http://stackoverflow.com/questions/19804233/android-autofocuscallback-is-not-being-called-or-not-returning/19921582#19921582 – ROHIT PARMAR Feb 26 '14 at 14:17

1 Answers1

0

You should copy the Zxing library source code into your project. The two packages that you will need for scanning QR code are

com.google.zxing.client.android

com.google.zxing.client.android.camera

After you have copied these packages you can call the QR code scanner activity from your activity.

private static final int LOGIN_SCAN_REQUEST = 0;
    /**
     * Call this method from anywhere in the activity
     */
    private void startScannerActivity()
    {
        Intent qrScannerIntent = new Intent(LoginActivity.this,
                CaptureActivity.class);
        startActivityForResult(qrScannerIntent,
                LOGIN_SCAN_REQUEST);
    }


/**
 * Result  is returned in this method after QR code scanning is complete
 */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == LOGIN_SCAN_REQUEST) {
        if (resultCode == RESULT_OK) {
                    String qrcodeString = data.getStringExtra(
                            Intents.Scan.RESULT).trim();
                    Log.i("QR String", qrcodeString);
             //Do what you want with QR Code here
        }

    }
}
Sheraz Ahmad Khilji
  • 8,300
  • 9
  • 52
  • 84
  • While that's not a direct violation of the license, I think it certainly violates the spirit of open source in general. Also, it won't work at all for this project. – KG6ZVP Feb 26 '14 at 09:44
  • @KG6ZVP ZXING is under Apache license so i dont think it will be a problem. BTW Why wont it work for your project ? – Sheraz Ahmad Khilji Feb 26 '14 at 10:00
  • As I said, but I stated that I was referring to the spirit of open source. Also, it must use a Fragment and must not be constrained by the layout of the UI used for that Activity. – KG6ZVP Feb 26 '14 at 10:03
  • onActivityResult() is also available in fragments – Sheraz Ahmad Khilji Feb 26 '14 at 10:13