dlazaro66 / QRCodeReaderView

Modification of ZXING Barcode Scanner project for easy Android QR-Code detection and AR purposes
1.9k stars 491 forks source link

openDriver method crashes and block UI threads #141

Closed 782891085 closed 7 years ago

782891085 commented 7 years ago

OpenDriver this method will cause some cell phone crashes, like some mobile phone version is less than 23, but it has its own permission management, such as HUAWEI H30-T00.

dlazaro66 commented 7 years ago

Didn't try with a device like this. Can you provide the stacktrace for the crash?

782891085 commented 7 years ago

here is the stacktrace in stacktrace.txt.But I don't think it's your problem, because it doesn't have permission, and developers call it and cause it to crash. Or you can throw an exception here to avoid crashing.

10-16 10:13:40.458 27197-27197/com.zjx.qrcodereaderviewtest E/AndroidRuntime: FATAL EXCEPTION: main java.lang.RuntimeException: Fail to connect to camera service at android.hardware.Camera.native_setup(Native Method) at android.hardware.Camera.<init>(Camera.java:434) at android.hardware.Camera.open(Camera.java:393) at com.google.zxing.client.android.camera.open.OpenCameraInterface.open(OpenCameraInterface.java:76) at com.google.zxing.client.android.camera.CameraManager.openDriver(CameraManager.java:104) at com.dlazaro66.qrcodereaderview.QRCodeReaderView.surfaceCreated(QRCodeReaderView.java:213) at android.view.SurfaceView.updateWindow(SurfaceView.java:622) at android.view.SurfaceView.access$000(SurfaceView.java:90) at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:185) at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:680) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2217) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1211) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5039) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:776) at android.view.Choreographer.doCallbacks(Choreographer.java:579) at android.view.Choreographer.doFrame(Choreographer.java:548) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:762) at android.os.Handler.handleCallback(Handler.java:800) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:194) at android.app.ActivityThread.main(ActivityThread.java:5433) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:525) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:922) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:689) at dalvik.system.NativeStart.main(Native Method) OpenDriver method blocking UI problem, I made some modifications to QRCodeReaderView and made a simple DEMO, but I'm not sure that I will make other problems caused by this modification. These files can be found in the attachment. Thank you. Here's my revised qrcodereaderview. `/*

import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Point; import android.graphics.PointF; import android.hardware.Camera; import android.os.AsyncTask; import android.util.AttributeSet; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.WindowManager;

import com.dlazaro66.qrcodereaderview.Orientation; import com.dlazaro66.qrcodereaderview.QRToViewPointTransformer; import com.google.zxing.BinaryBitmap; import com.google.zxing.ChecksumException; import com.google.zxing.DecodeHintType; import com.google.zxing.FormatException; import com.google.zxing.NotFoundException; import com.google.zxing.PlanarYUVLuminanceSource; import com.google.zxing.Result; import com.google.zxing.ResultPoint; import com.google.zxing.client.android.camera.CameraManager; import com.google.zxing.common.HybridBinarizer; import com.google.zxing.qrcode.QRCodeReader;

import java.io.IOException; import java.lang.ref.WeakReference; import java.util.Map; import static android.hardware.Camera.getCameraInfo;

public class QRCodeReaderView extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback { private QRCodeReaderView.OnCameraOpenDriverErrorListener mOnCameraOpenDriverErrorListener;

public interface OnCameraOpenDriverErrorListener {
    void OnCameraOpenDriverError(Exception e);
}

public void setOnCameraOpenDriverErrorListener(QRCodeReaderView.OnCameraOpenDriverErrorListener onCameraOpenDriverErrorListener) {
    mOnCameraOpenDriverErrorListener = onCameraOpenDriverErrorListener;
}

public interface OnQRCodeReadListener {

    void onQRCodeRead(String text, PointF[] points);
}

private QRCodeReaderView.OnQRCodeReadListener mOnQRCodeReadListener;

private static final String TAG = QRCodeReaderView.class.getName();

private QRCodeReader mQRCodeReader;
private int mPreviewWidth;
private int mPreviewHeight;
private CameraManager mCameraManager;
private boolean mQrDecodingEnabled = true;
private DecodeFrameTask decodeFrameTask;
private Map<DecodeHintType, Object> decodeHints;

public QRCodeReaderView(Context context) {
    this(context, null);
}

public QRCodeReaderView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initCamera();
}

public void initCamera() {
    if (isInEditMode()) {
        return;
    }
    if (checkCameraHardware()) {
        mCameraManager = new CameraManager(getContext());
        mCameraManager.setPreviewCallback(this);
        setBackCamera();
        getHolder().addCallback(this);
    } else {
        throw new RuntimeException("Error: Camera not found");
    }
}

/**
 * Set the callback to return decoding result
 *
 * @param onQRCodeReadListener the listener
 */
public void setOnQRCodeReadListener(QRCodeReaderView.OnQRCodeReadListener onQRCodeReadListener) {
    mOnQRCodeReadListener = onQRCodeReadListener;
}

/**
 * Set QR decoding enabled/disabled.
 * default value is true
 *
 * @param qrDecodingEnabled decoding enabled/disabled.
 */
public void setQRDecodingEnabled(boolean qrDecodingEnabled) {
    this.mQrDecodingEnabled = qrDecodingEnabled;
}

/**
 * Set QR hints required for decoding
 *
 * @param decodeHints hints for decoding qrcode
 */
public void setDecodeHints(Map<DecodeHintType, Object> decodeHints) {
    this.decodeHints = decodeHints;
}

/**
 * Starts camera preview and decoding
 */
public void startCamera() {
    if (mCameraManager != null) {
        mCameraManager.startPreview();
    }
}

/**
 * Stop camera preview and decoding
 */
public void stopCamera() {
    if (mCameraManager != null) {
        mCameraManager.stopPreview();
    }
}

/**
 * Set Camera autofocus interval value
 * default value is 5000 ms.
 *
 * @param autofocusIntervalInMs autofocus interval value
 */
public void setAutofocusInterval(long autofocusIntervalInMs) {
    if (mCameraManager != null) {
        mCameraManager.setAutofocusInterval(autofocusIntervalInMs);
    }
}

/**
 * Trigger an auto focus
 */
public void forceAutoFocus() {
    if (mCameraManager != null) {
        mCameraManager.forceAutoFocus();
    }
}

/**
 * Set Torch enabled/disabled.
 * default value is false
 *
 * @param enabled torch enabled/disabled.
 */
public void setTorchEnabled(boolean enabled) {
    if (mCameraManager != null) {
        mCameraManager.setTorchEnabled(enabled);
    }
}

/**
 * Allows user to specify the camera ID, rather than determine
 * it automatically based on available cameras and their orientation.
 *
 * @param cameraId camera ID of the camera to use. A negative value means "no preference".
 */
public void setPreviewCameraId(int cameraId) {
    if (mCameraManager != null) {
        mCameraManager.setPreviewCameraId(cameraId);
    }
}

/**
 * Camera preview from device back camera
 */
public void setBackCamera() {
    setPreviewCameraId(Camera.CameraInfo.CAMERA_FACING_BACK);
}

/**
 * Camera preview from device front camera
 */
public void setFrontCamera() {
    setPreviewCameraId(Camera.CameraInfo.CAMERA_FACING_FRONT);
}

@Override
public void onDetachedFromWindow() {
    super.onDetachedFromWindow();

    if (decodeFrameTask != null) {
        decodeFrameTask.cancel(true);
        decodeFrameTask = null;
    }
}

/****************************************************
 * SurfaceHolder.Callback,Camera.PreviewCallback
 ****************************************************/

@Override
public void surfaceCreated(final SurfaceHolder holder) {
    startInit();
}

public void startInit() {
    new InitThread().start();
}

private class InitThread extends Thread {
    @Override
    public void run() {
        super.run();
        init(getHolder());
    }
}

private void init(SurfaceHolder holder) {
    long s = System.currentTimeMillis();
    if (mCameraManager == null) {
        return;
    }
    try {
        // Indicate camera, our View dimensions
        mCameraManager.openDriver(holder, QRCodeReaderView.this.getWidth(), QRCodeReaderView.this.getHeight());
    } catch (IOException e1) {
        Log.w(TAG, "Can not openDriver: " + e1.getMessage());
        mCameraManager.closeDriver();
    } catch (final RuntimeException e) {
        //zjx:If don't get permission, it will crash
        Log.w(TAG, "Can not openDriver: " + e.getMessage());
        if (mOnCameraOpenDriverErrorListener != null) {
            post(new Runnable() {
                @Override
                public void run() {
                    mOnCameraOpenDriverErrorListener.OnCameraOpenDriverError(e);
                }
            });

        }
        mCameraManager.closeDriver();
    }
    try {
        mQRCodeReader = new QRCodeReader();
        mCameraManager.startPreview();
    } catch (Exception e2) {
        Log.e(TAG, "Exception: " + e2.getMessage());
        mCameraManager.closeDriver();
    }

    if (holder.getSurface() == null) {
        Log.e(TAG, "Error: preview surface does not exist");
        return;
    }
    if (mCameraManager == null) {
        return;
    }

    if (mCameraManager.getPreviewSize() == null) {
        Log.e(TAG, "Error: preview size does not exist");
        return;
    }

    mPreviewWidth = mCameraManager.getPreviewSize().x;
    mPreviewHeight = mCameraManager.getPreviewSize().y;

    mCameraManager.stopPreview();

    // Fix the camera sensor rotation
    mCameraManager.setPreviewCallback(this);
    mCameraManager.setDisplayOrientation(getCameraDisplayOrientation());

    mCameraManager.startPreview();
    long e = System.currentTimeMillis();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    Log.e(TAG, "surfaceChanged:end ");
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    mCameraManager.setPreviewCallback(null);
    mCameraManager.stopPreview();
    mCameraManager.closeDriver();
}

// Called when camera take a frame
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
    if (!mQrDecodingEnabled || decodeFrameTask != null
            && decodeFrameTask.getStatus() == AsyncTask.Status.RUNNING) {
        return;
    }

    decodeFrameTask = new DecodeFrameTask(this, decodeHints);
    decodeFrameTask.execute(data);
}

/**
 * Check if this device has a camera
 */
private boolean checkCameraHardware() {
    if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
        // this device has a camera
        return true;
    } else if (getContext().getPackageManager()
            .hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
        // this device has a front camera
        return true;
    } else {
        // this device has any camera
        return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
    }
}

/**
 * Fix for the camera Sensor on some devices (ex.: Nexus 5x)
 */
@SuppressWarnings("deprecation")
private int getCameraDisplayOrientation() {
    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.GINGERBREAD) {
        return 90;
    }

    Camera.CameraInfo info = new Camera.CameraInfo();
    getCameraInfo(mCameraManager.getPreviewCameraId(), info);
    WindowManager windowManager =
            (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    int rotation = windowManager.getDefaultDisplay().getRotation();
    int degrees = 0;
    switch (rotation) {
        case Surface.ROTATION_0:
            degrees = 0;
            break;
        case Surface.ROTATION_90:
            degrees = 90;
            break;
        case Surface.ROTATION_180:
            degrees = 180;
            break;
        case Surface.ROTATION_270:
            degrees = 270;
            break;
        default:
            break;
    }

    int result;
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        result = (info.orientation + degrees) % 360;
        result = (360 - result) % 360;  // compensate the mirror
    } else {  // back-facing
        result = (info.orientation - degrees + 360) % 360;
    }
    return result;
}

private static class DecodeFrameTask extends AsyncTask<byte[], Void, Result> {

    private final WeakReference<QRCodeReaderView> viewRef;
    private final WeakReference<Map<DecodeHintType, Object>> hintsRef;
    private final QRToViewPointTransformer qrToViewPointTransformer =
            new QRToViewPointTransformer();

    public DecodeFrameTask(QRCodeReaderView view, Map<DecodeHintType, Object> hints) {
        viewRef = new WeakReference<>(view);
        hintsRef = new WeakReference<>(hints);
    }

    @Override
    protected Result doInBackground(byte[]... params) {
        final QRCodeReaderView view = viewRef.get();
        if (view == null) {
            return null;
        }

        final PlanarYUVLuminanceSource source =
                view.mCameraManager.buildLuminanceSource(params[0], view.mPreviewWidth,
                        view.mPreviewHeight);

        final HybridBinarizer hybBin = new HybridBinarizer(source);
        final BinaryBitmap bitmap = new BinaryBitmap(hybBin);

        try {
            return view.mQRCodeReader.decode(bitmap, hintsRef.get());
        } catch (ChecksumException e) {
            Log.d(TAG, "ChecksumException", e);
        } catch (NotFoundException e) {
            Log.d(TAG, "No QR Code found");
        } catch (FormatException e) {
            Log.d(TAG, "FormatException", e);
        } catch (Exception e) {

        } finally {
            view.mQRCodeReader.reset();
        }

        return null;
    }

    @Override
    protected void onPostExecute(Result result) {
        super.onPostExecute(result);

        final QRCodeReaderView view = viewRef.get();

        // Notify we found a QRCode
        if (view != null && result != null && view.mOnQRCodeReadListener != null) {
            // Transform resultPoints to View coordinates
            final PointF[] transformedPoints =
                    transformToViewCoordinates(view, result.getResultPoints());
            view.mOnQRCodeReadListener.onQRCodeRead(result.getText(), transformedPoints);
        }
    }

    /**
     * Transform result to surfaceView coordinates
     * <p>
     * This method is needed because coordinates are given in landscape camera coordinates when
     * device is in portrait mode and different coordinates otherwise.
     *
     * @return a new PointF array with transformed points
     */
    private PointF[] transformToViewCoordinates(QRCodeReaderView view, ResultPoint[] resultPoints) {
        int orientationDegrees = view.getCameraDisplayOrientation();
        Orientation orientation =
                orientationDegrees == 90 || orientationDegrees == 270 ? Orientation.PORTRAIT
                        : Orientation.LANDSCAPE;
        Point viewSize = new Point(view.getWidth(), view.getHeight());
        Point cameraPreviewSize = view.mCameraManager.getPreviewSize();
        boolean isMirrorCamera =
                view.mCameraManager.getPreviewCameraId() == Camera.CameraInfo.CAMERA_FACING_FRONT;

        return qrToViewPointTransformer.transform(resultPoints, isMirrorCamera, orientation, viewSize,
                cameraPreviewSize);
    }
}

}

`

dlazaro66 commented 7 years ago

It's definitely a permission grant issue, please take a look at the example code, and make sure you ask for it before using the library