Skip to content

Instantly share code, notes, and snippets.

@n4cr
Last active July 20, 2017 06:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save n4cr/97b120d72db16340a50c to your computer and use it in GitHub Desktop.
Save n4cr/97b120d72db16340a50c to your computer and use it in GitHub Desktop.
/*
* Copyright 2012-2014 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.graybits.wallet.common;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.annotation.SuppressLint;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.PreviewCallback;
import android.view.SurfaceHolder;
import com.google.zxing.PlanarYUVLuminanceSource;
/**
* @author Andreas Schildbach
*/
public final class CameraManager {
private static final int MIN_FRAME_SIZE = 240;
private static final int MAX_FRAME_SIZE = 600;
private static final int MIN_PREVIEW_PIXELS = 470 * 320; // normal screen
private static final int MAX_PREVIEW_PIXELS = 1280 * 720;
private Camera camera;
private Camera.Size cameraResolution;
private Rect frame;
private Rect framePreview;
// private static final Logger log = LoggerFactory.getLogger(CameraManager.class);
public Rect getFrame() {
return frame;
}
public Rect getFramePreview() {
return framePreview;
}
public Camera open(final SurfaceHolder holder, final boolean continuousAutoFocus) throws IOException {
// try back-facing camera
camera = Camera.open();
// fall back to using front-facing camera
if (camera == null) {
final int cameraCount = Camera.getNumberOfCameras();
final CameraInfo cameraInfo = new CameraInfo();
// search for front-facing camera
for (int i = 0; i < cameraCount; i++) {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
camera = Camera.open(i);
break;
}
}
}
camera.setPreviewDisplay(holder);
final Camera.Parameters parameters = camera.getParameters();
final Rect surfaceFrame = holder.getSurfaceFrame();
cameraResolution = findBestPreviewSizeValue(parameters, surfaceFrame);
final int surfaceWidth = surfaceFrame.width();
final int surfaceHeight = surfaceFrame.height();
final int rawSize = Math.min(surfaceWidth * 2 / 3, surfaceHeight * 2 / 3);
final int frameSize = Math.max(MIN_FRAME_SIZE, Math.min(MAX_FRAME_SIZE, rawSize));
final int leftOffset = (surfaceWidth - frameSize) / 2;
final int topOffset = (surfaceHeight - frameSize) / 2;
frame = new Rect(leftOffset, topOffset, leftOffset + frameSize, topOffset + frameSize);
framePreview = new Rect(frame.left * cameraResolution.width / surfaceWidth, frame.top * cameraResolution.height / surfaceHeight, frame.right
* cameraResolution.width / surfaceWidth, frame.bottom * cameraResolution.height / surfaceHeight);
final String savedParameters = parameters == null ? null : parameters.flatten();
try {
setDesiredCameraParameters(camera, cameraResolution, continuousAutoFocus);
} catch (final RuntimeException x) {
if (savedParameters != null) {
final Camera.Parameters parameters2 = camera.getParameters();
parameters2.unflatten(savedParameters);
try {
camera.setParameters(parameters2);
setDesiredCameraParameters(camera, cameraResolution, continuousAutoFocus);
} catch (final RuntimeException x2) {
// log.info("problem setting camera parameters", x2);
}
}
}
camera.startPreview();
return camera;
}
public void close() {
if (camera != null) {
camera.stopPreview();
camera.release();
}
}
private static final Comparator<Camera.Size> numPixelComparator = new Comparator<Camera.Size>() {
@Override
public int compare(final Camera.Size size1, final Camera.Size size2) {
final int pixels1 = size1.height * size1.width;
final int pixels2 = size2.height * size2.width;
if (pixels1 < pixels2)
return 1;
else if (pixels1 > pixels2)
return -1;
else
return 0;
}
};
private static Camera.Size findBestPreviewSizeValue(final Camera.Parameters parameters, Rect surfaceResolution) {
if (surfaceResolution.height() > surfaceResolution.width())
surfaceResolution = new Rect(0, 0, surfaceResolution.height(), surfaceResolution.width());
final float screenAspectRatio = (float) surfaceResolution.width() / (float) surfaceResolution.height();
final List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
if (rawSupportedSizes == null)
return parameters.getPreviewSize();
// sort by size, descending
final List<Camera.Size> supportedPreviewSizes = new ArrayList<Camera.Size>(rawSupportedSizes);
Collections.sort(supportedPreviewSizes, numPixelComparator);
Camera.Size bestSize = null;
float diff = Float.POSITIVE_INFINITY;
for (final Camera.Size supportedPreviewSize : supportedPreviewSizes) {
final int realWidth = supportedPreviewSize.width;
final int realHeight = supportedPreviewSize.height;
final int realPixels = realWidth * realHeight;
if (realPixels < MIN_PREVIEW_PIXELS || realPixels > MAX_PREVIEW_PIXELS)
continue;
final boolean isCandidatePortrait = realWidth < realHeight;
final int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
final int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
if (maybeFlippedWidth == surfaceResolution.width() && maybeFlippedHeight == surfaceResolution.height())
return supportedPreviewSize;
final float aspectRatio = (float) maybeFlippedWidth / (float) maybeFlippedHeight;
final float newDiff = Math.abs(aspectRatio - screenAspectRatio);
if (newDiff < diff) {
bestSize = supportedPreviewSize;
diff = newDiff;
}
}
if (bestSize != null)
return bestSize;
else
return parameters.getPreviewSize();
}
@SuppressLint("InlinedApi")
private static void setDesiredCameraParameters(final Camera camera, final Camera.Size cameraResolution, final boolean continuousAutoFocus) {
final Camera.Parameters parameters = camera.getParameters();
if (parameters == null)
return;
final List<String> supportedFocusModes = parameters.getSupportedFocusModes();
final String focusMode = continuousAutoFocus ? findValue(supportedFocusModes, Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO, Camera.Parameters.FOCUS_MODE_AUTO, Camera.Parameters.FOCUS_MODE_MACRO) : findValue(
supportedFocusModes, Camera.Parameters.FOCUS_MODE_AUTO, Camera.Parameters.FOCUS_MODE_MACRO);
if (focusMode != null)
parameters.setFocusMode(focusMode);
parameters.setPreviewSize(cameraResolution.width, cameraResolution.height);
camera.setParameters(parameters);
}
public void requestPreviewFrame(final PreviewCallback callback) {
camera.setOneShotPreviewCallback(callback);
}
public PlanarYUVLuminanceSource buildLuminanceSource(final byte[] data) {
return new PlanarYUVLuminanceSource(data, cameraResolution.width, cameraResolution.height, framePreview.left, framePreview.top,
framePreview.width(), framePreview.height(), false);
}
public void setTorch(final boolean enabled) {
if (enabled != getTorchEnabled(camera))
setTorchEnabled(camera, enabled);
}
private static boolean getTorchEnabled(final Camera camera) {
final Camera.Parameters parameters = camera.getParameters();
if (parameters != null) {
final String flashMode = camera.getParameters().getFlashMode();
return flashMode != null && (Camera.Parameters.FLASH_MODE_ON.equals(flashMode) || Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode));
}
return false;
}
private static void setTorchEnabled(final Camera camera, final boolean enabled) {
final Camera.Parameters parameters = camera.getParameters();
final List<String> supportedFlashModes = parameters.getSupportedFlashModes();
if (supportedFlashModes != null) {
final String flashMode;
if (enabled)
flashMode = findValue(supportedFlashModes, Camera.Parameters.FLASH_MODE_TORCH, Camera.Parameters.FLASH_MODE_ON);
else
flashMode = findValue(supportedFlashModes, Camera.Parameters.FLASH_MODE_OFF);
if (flashMode != null) {
camera.cancelAutoFocus(); // autofocus can cause conflict
parameters.setFlashMode(flashMode);
camera.setParameters(parameters);
}
}
}
private static String findValue(final Collection<String> values, final String... valuesToFind) {
for (final String valueToFind : valuesToFind)
if (values.contains(valueToFind))
return valueToFind;
return null;
}
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- scanner -->
<color name="scan_mask">#60000000</color>
<color name="scan_laser">#cc0000</color>
<color name="scan_dot">#ff6600</color>
<color name="scan_result_view">#b0000000</color>
<color name="scan_result_dots">#c099cc00</color>
</resources>
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout android:layout_width="match_parent"
android:layout_height="300dp">
<SurfaceView
android:id="@+id/scan_activity_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"/>
<com.graybits.wallet.common.ScannerView
android:id="@+id/scan_activity_mask"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:text="Do something"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
package com.graybits.wallet.common;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
import android.os.Vibrator;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.ResultPointCallback;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.graybits.wallet.R;
import java.io.IOException;
import java.util.EnumMap;
import java.util.Map;
import butterknife.InjectView;
/**
* Created by nasir on 9/15/14.
*/
public class ScanFragment extends BaseFragment implements SurfaceHolder.Callback {
public static final String INTENT_EXTRA_RESULT = "result";
private static final long VIBRATE_DURATION = 50L;
private static final long AUTO_FOCUS_INTERVAL_MS = 2500L;
private final CameraManager cameraManager = new CameraManager();
@InjectView(R.id.scan_activity_mask)
ScannerView scannerView;
private SurfaceHolder surfaceHolder;
private Vibrator vibrator;
private HandlerThread cameraThread;
private Handler cameraHandler;
private static final int DIALOG_CAMERA_PROBLEM = 0;
private static boolean DISABLE_CONTINUOUS_AUTOFOCUS = Build.MODEL.equals("GT-I9100") // Galaxy S2
|| Build.MODEL.equals("SGH-T989") // Galaxy S2
|| Build.MODEL.equals("SGH-T989D") // Galaxy S2 X
|| Build.MODEL.equals("SAMSUNG-SGH-I727") // Galaxy S2 Skyrocket
|| Build.MODEL.equals("GT-I9300") // Galaxy S3
|| Build.MODEL.equals("GT-N7000"); // Galaxy Note
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.scan_activity, null);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
vibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
cameraThread = new HandlerThread("cameraThread", Process.THREAD_PRIORITY_BACKGROUND);
cameraThread.start();
cameraHandler = new Handler(cameraThread.getLooper());
final SurfaceView surfaceView = (SurfaceView) getView().findViewById(R.id.scan_activity_preview);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@Override
public void onResume() {
super.onResume();
// cameraThread = new HandlerThread("cameraThread", Process.THREAD_PRIORITY_BACKGROUND);
// cameraThread.start();
// cameraHandler = new Handler(cameraThread.getLooper());
// final SurfaceView surfaceView = (SurfaceView) getView().findViewById(R.id.scan_activity_preview);
// surfaceHolder = surfaceView.getHolder();
// surfaceHolder.addCallback(this);
// surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@Override
public void onPause() {
cameraHandler.post(closeRunnable);
surfaceHolder.removeCallback(this);
super.onPause();
}
@Override
public void surfaceCreated(final SurfaceHolder holder) {
cameraHandler.post(openRunnable);
}
@Override
public void surfaceDestroyed(final SurfaceHolder holder) {
}
@Override
public void surfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {
}
public void handleResult(final Result scanResult, final Bitmap thumbnailImage, final float thumbnailScaleFactor) {
vibrator.vibrate(VIBRATE_DURATION);
// superimpose dots to highlight the key features of the qr code
final ResultPoint[] points = scanResult.getResultPoints();
if (points != null && points.length > 0) {
final Paint paint = new Paint();
paint.setColor(getResources().getColor(R.color.scan_result_dots));
paint.setStrokeWidth(10.0f);
final Canvas canvas = new Canvas(thumbnailImage);
canvas.scale(thumbnailScaleFactor, thumbnailScaleFactor);
for (final ResultPoint point : points)
canvas.drawPoint(point.getX(), point.getY(), paint);
}
scannerView.drawResultBitmap(thumbnailImage);
final Intent result = new Intent();
result.putExtra(INTENT_EXTRA_RESULT, scanResult.getText());
getActivity().setResult(Activity.RESULT_OK, result);
new Handler().post(new Runnable() {
@Override
public void run() {
getActivity().finish();
}
});
}
private final Runnable openRunnable = new Runnable() {
@Override
public void run() {
try {
final Camera camera = cameraManager.open(surfaceHolder, !DISABLE_CONTINUOUS_AUTOFOCUS);
camera.setDisplayOrientation(90);
final Rect framingRect = cameraManager.getFrame();
final Rect framingRectInPreview = cameraManager.getFramePreview();
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
scannerView.setFraming(framingRect, framingRectInPreview);
}
});
final String focusMode = camera.getParameters().getFocusMode();
final boolean nonContinuousAutoFocus = Camera.Parameters.FOCUS_MODE_AUTO.equals(focusMode)
|| Camera.Parameters.FOCUS_MODE_MACRO.equals(focusMode);
if (nonContinuousAutoFocus)
cameraHandler.post(new AutoFocusRunnable(camera));
cameraHandler.post(fetchAndDecodeRunnable);
} catch (final IOException x) {
// log.info("problem opening camera", x);
error(x.getMessage(), x);
// showDialog(DIALOG_CAMERA_PROBLEM);
throw new IllegalStateException();
} catch (final RuntimeException x) {
error(x.getMessage(), x);
// log.info("problem opening camera", x);
// showDialog(DIALOG_CAMERA_PROBLEM);
throw new IllegalStateException();
}
}
};
private final Runnable closeRunnable = new Runnable() {
@Override
public void run() {
cameraManager.close();
// cancel background thread
cameraHandler.removeCallbacksAndMessages(null);
cameraThread.quit();
}
};
private final class AutoFocusRunnable implements Runnable {
private final Camera camera;
public AutoFocusRunnable(final Camera camera) {
this.camera = camera;
}
@Override
public void run() {
camera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(final boolean success, final Camera camera) {
// schedule again
cameraHandler.postDelayed(AutoFocusRunnable.this, AUTO_FOCUS_INTERVAL_MS);
}
});
}
}
private final Runnable fetchAndDecodeRunnable = new Runnable() {
private final QRCodeReader reader = new QRCodeReader();
private final Map<DecodeHintType, Object> hints = new EnumMap<DecodeHintType, Object>(DecodeHintType.class);
@Override
public void run() {
cameraManager.requestPreviewFrame(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(final byte[] data, final Camera camera) {
decode(data);
}
});
}
private void decode(final byte[] data) {
final PlanarYUVLuminanceSource source = cameraManager.buildLuminanceSource(data);
final BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
try {
hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, new ResultPointCallback() {
@Override
public void foundPossibleResultPoint(final ResultPoint dot) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
scannerView.addDot(dot);
}
});
}
});
final Result scanResult = reader.decode(bitmap, hints);
final int thumbnailWidth = source.getThumbnailWidth();
final int thumbnailHeight = source.getThumbnailHeight();
final float thumbnailScaleFactor = (float) thumbnailWidth / source.getWidth();
final Bitmap thumbnailImage = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888);
thumbnailImage.setPixels(source.renderThumbnail(), 0, thumbnailWidth, 0, 0, thumbnailWidth, thumbnailHeight);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
handleResult(scanResult, thumbnailImage, thumbnailScaleFactor);
}
});
} catch (final ReaderException x) {
// retry
cameraHandler.post(fetchAndDecodeRunnable);
} finally {
reader.reset();
}
}
};
}
/*
* Copyright 2012-2014 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.graybits.wallet.common;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.*;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.view.View;
import com.google.zxing.ResultPoint;
import com.graybits.wallet.R;
/**
* @author Andreas Schildbach
*/
public class ScannerView extends View {
private static final long LASER_ANIMATION_DELAY_MS = 100l;
private static final int DOT_OPACITY = 0xa0;
private static final int DOT_SIZE = 8;
private static final int DOT_TTL_MS = 500;
private final Paint maskPaint;
private final Paint laserPaint;
private final Paint dotPaint;
private Bitmap resultBitmap;
private final int maskColor;
private final int resultColor;
private final Map<ResultPoint, Long> dots = new HashMap<ResultPoint, Long>(16);
private Rect frame, framePreview;
public ScannerView(final Context context, final AttributeSet attrs) {
super(context, attrs);
final Resources res = getResources();
maskColor = res.getColor(R.color.scan_mask);
resultColor = res.getColor(R.color.scan_result_view);
final int laserColor = res.getColor(R.color.scan_laser);
final int dotColor = res.getColor(R.color.scan_dot);
maskPaint = new Paint();
maskPaint.setStyle(Style.FILL);
laserPaint = new Paint();
laserPaint.setColor(laserColor);
laserPaint.setStrokeWidth(DOT_SIZE);
laserPaint.setStyle(Style.STROKE);
dotPaint = new Paint();
dotPaint.setColor(dotColor);
dotPaint.setAlpha(DOT_OPACITY);
dotPaint.setStyle(Style.STROKE);
dotPaint.setStrokeWidth(DOT_SIZE);
dotPaint.setAntiAlias(true);
}
public void setFraming(final Rect frame, final Rect framePreview) {
this.frame = frame;
this.framePreview = framePreview;
invalidate();
}
public void drawResultBitmap(final Bitmap bitmap) {
resultBitmap = bitmap;
invalidate();
}
public void addDot(final ResultPoint dot) {
dots.put(dot, System.currentTimeMillis());
invalidate();
}
@Override
public void onDraw(final Canvas canvas) {
if (frame == null)
return;
final long now = System.currentTimeMillis();
final int width = canvas.getWidth();
final int height = canvas.getHeight();
// draw mask darkened
maskPaint.setColor(resultBitmap != null ? resultColor : maskColor);
canvas.drawRect(0, 0, width, frame.top, maskPaint);
canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, maskPaint);
canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, maskPaint);
canvas.drawRect(0, frame.bottom + 1, width, height, maskPaint);
if (resultBitmap != null) {
canvas.drawBitmap(resultBitmap, null, frame, maskPaint);
} else {
// draw red "laser scanner" to show decoding is active
final boolean laserPhase = (now / 600) % 2 == 0;
laserPaint.setAlpha(laserPhase ? 160 : 255);
canvas.drawRect(frame, laserPaint);
// draw points
final int frameLeft = frame.left;
final int frameTop = frame.top;
final float scaleX = frame.width() / (float) framePreview.width();
final float scaleY = frame.height() / (float) framePreview.height();
for (final Iterator<Map.Entry<ResultPoint, Long>> i = dots.entrySet().iterator(); i.hasNext(); ) {
final Map.Entry<ResultPoint, Long> entry = i.next();
final long age = now - entry.getValue();
if (age < DOT_TTL_MS) {
dotPaint.setAlpha((int) ((DOT_TTL_MS - age) * 256 / DOT_TTL_MS));
final ResultPoint point = entry.getKey();
canvas.drawPoint(frameLeft + (int) (point.getX() * scaleX), frameTop + (int) (point.getY() * scaleY), dotPaint);
} else {
i.remove();
}
}
// schedule redraw
postInvalidateDelayed(LASER_ANIMATION_DELAY_MS);
}
}
}
@naresh1253
Copy link

I tried running this example, but the camera do not seem to be working, I get the first half of the screen black and then nothing happens. Could you please tell me what am i missing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment