-
-
Save indraAsLesmana/f7f9b2c48055e3def3750b8fdcc3b353 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
}; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment