Android 基于Zxing的二维码扫描优化
最近公司项目App中要集成二维码扫描来适应在户外工作的时候,对码头集装箱等上面贴的A4纸张打印的二维码进行识别, 一般App二维码集成后,能扫出来就不管了,但是我们在集成成功后,根据用户反馈,在户外的环境下,很多二维码识别不了,或者识别速度慢,我们自己也是适用了一下,发现也确实是这样.
一般造成这个识别不出来的原因,我们总结了以下几点:
- A4纸张打印的标签二维码, 本来打印就不是特别清晰,有些像素点,不一定都打印了出来,
- 户外环境,多变,灰尘,粉尘影响
- 日晒雨淋,还有各种划痕,造成了二维码破损和有污渍
- Android手机配置不一样,有的手机好一点,像素高,有的用户的像素低
大概就是这些,但是用基于QBar(在Zxing上做了优化)的微信,却能很快的识别出上面几种情况造成的二维码; 基于libqrencode 库集成的支付宝或者钉钉二维码扫描,一样也能识别出来;还有IOS也就是调用系统的扫描,也一样能够扫描出来,为啥我们大android不行?
老板不管这些,只是说了,别人的可以,为啥你的不可以,那就是你的问题.......
网上找了很多各种几千个赞的第三方集成的二维码,根本就不能满足上面的需求,当时感觉真心不知道怎么办好了.
唯独网上找的这个还可以一点:https://github.com/vondear/RxTool 但是破损一些的还是扫描不出来;那个网上几千个赞的一片枫叶的 ,这种环境下的二维码扫描根本边都摸不到.
还有郭林推荐的:https://github.com/al4fun/SimpleScanner 这个库,虽然是大神推荐的,但是比上面的这个,还要差那么好几点,郭林的推荐的二维码扫描链接:https://mp.weixin.qq.com/s/aPqSK1FlsPiENzSE48BVUA
没办法,最后只能自己动手,网上找的,没有找到合适的,目前我们修改的二维码扫描基本可以做到:除了破损的识别不了,其他的都能识别,就是有时候速度慢了点,要多对一下焦,勉强能够比上面好那么一点点而已.
代码如下: (这里面很多类都在原有类的基础上有改动,虽然类名相同,但是里面的方法有些有变动!)
build.gradle
dependencies{api fileTree(include: ['*.jar'], dir: 'libs')api files('libs/core-3.3.0.jar')// provided 'com.android.support:appcompat-v7:26.1.0'compileOnly 'com.android.support:design:26.1.0'compileOnly 'com.android.support:support-vector-drawable:26.1.0'
}
文件目录如下:
ZxingConfig.java
public class ZxingConfig implements Serializable {/*是否播放声音*/private boolean isPlayBeep = true;/*是否震动*/private boolean isShake = false;/*是否显示下方的其他功能布局*/private boolean isShowbottomLayout = true;/*是否显示闪光灯按钮*/private boolean isShowFlashLight = true;/*是否显示相册按钮*/private boolean isShowAlbum = true;public boolean isPlayBeep() {return isPlayBeep;}public void setPlayBeep(boolean playBeep) {isPlayBeep = playBeep;}public boolean isShake() {return isShake;}public void setShake(boolean shake) {isShake = shake;}public boolean isShowbottomLayout() {return isShowbottomLayout;}public void setShowbottomLayout(boolean showbottomLayout) {isShowbottomLayout = showbottomLayout;}public boolean isShowFlashLight() {return isShowFlashLight;}public void setShowFlashLight(boolean showFlashLight) {isShowFlashLight = showFlashLight;}public boolean isShowAlbum() {return isShowAlbum;}public void setShowAlbum(boolean showAlbum) {isShowAlbum = showAlbum;}
}
CameraFacing.java
public enum CameraFacing {BACK, // must be value 0!FRONT, // must be value 1!}
OpenCamera.java
public final class OpenCamera {private final int index;private final Camera camera;private final CameraFacing facing;private final int orientation;public OpenCamera(int index, Camera camera, CameraFacing facing, int orientation) {this.index = index;this.camera = camera;this.facing = facing;this.orientation = orientation;}public Camera getCamera() {return camera;}public CameraFacing getFacing() {return facing;}public int getOrientation() {return orientation;}@Overridepublic String toString() {return "Camera #" + index + " : " + facing + ',' + orientation;}}
OpenCameraInterface.java
public final class OpenCameraInterface {private static final String TAG = OpenCameraInterface.class.getName();private OpenCameraInterface() {}/*** For {@link #open(int)}, means no preference for which camera to open.*/public static final int NO_REQUESTED_CAMERA = -1;/*** Opens the requested camera with {@link Camera#open(int)}, if one exists.** @param cameraId camera ID of the camera to use. A negative value* or {@link #NO_REQUESTED_CAMERA} means "no preference", in which case a rear-facing* camera is returned if possible or else any camera* @return handle to {@link OpenCamera} that was opened*/public static OpenCamera open(int cameraId) {int numCameras = Camera.getNumberOfCameras();if (numCameras == 0) {Log.w(TAG, "No cameras!");return null;}boolean explicitRequest = cameraId >= 0;Camera.CameraInfo selectedCameraInfo = null;int index;if (explicitRequest) {index = cameraId;selectedCameraInfo = new Camera.CameraInfo();Camera.getCameraInfo(index, selectedCameraInfo);} else {index = 0;while (index < numCameras) {Camera.CameraInfo cameraInfo = new Camera.CameraInfo();Camera.getCameraInfo(index, cameraInfo);CameraFacing reportedFacing = CameraFacing.values()[cameraInfo.facing];if (reportedFacing == CameraFacing.BACK) {selectedCameraInfo = cameraInfo;break;}index++;}}Camera camera;if (index < numCameras) {Log.i(TAG, "Opening camera #" + index);camera = Camera.open(index);} else {if (explicitRequest) {Log.w(TAG, "Requested camera does not exist: " + cameraId);camera = null;} else {Log.i(TAG, "No camera facing " + CameraFacing.BACK + "; returning camera #0");camera = Camera.open(0);selectedCameraInfo = new Camera.CameraInfo();Camera.getCameraInfo(0, selectedCameraInfo);}}if (camera == null) {return null;}return new OpenCamera(index,camera,CameraFacing.values()[selectedCameraInfo.facing],selectedCameraInfo.orientation);}}
AutoFocusManager.java
final class AutoFocusManager implements Camera.AutoFocusCallback {private static final String TAG = AutoFocusManager.class.getSimpleName();private static final long AUTO_FOCUS_INTERVAL_MS = 2000L;private static final Collection<String> FOCUS_MODES_CALLING_AF;static {FOCUS_MODES_CALLING_AF = new ArrayList<>(2);FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO);FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO);}private boolean stopped;private boolean focusing;private final boolean useAutoFocus;private final Camera camera;private AsyncTask<?,?,?> outstandingTask;AutoFocusManager(Context context, Camera camera) {this.camera = camera;SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);String currentFocusMode = camera.getParameters().getFocusMode();useAutoFocus =sharedPrefs.getBoolean(QRConstants.KEY_AUTO_FOCUS, true) &&FOCUS_MODES_CALLING_AF.contains(currentFocusMode);Log.i(TAG, "Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus);start();}@Overridepublic synchronized void onAutoFocus(boolean success, Camera theCamera) {focusing = false;autoFocusAgainLater();}private synchronized void autoFocusAgainLater() {if (!stopped && outstandingTask == null) {AutoFocusTask newTask = new AutoFocusTask();try {newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);outstandingTask = newTask;} catch (RejectedExecutionException ree) {Log.w(TAG, "Could not request auto focus", ree);}}}synchronized void start() {
// if (useAutoFocus) {outstandingTask = null;if (!stopped && !focusing) {try {camera.autoFocus(this);focusing = true;} catch (RuntimeException re) {// Have heard RuntimeException reported in Android 4.0.x+; continue?Log.w(TAG, "Unexpected exception while focusing", re);// Try again later to keep cycle goingautoFocusAgainLater();}}
// }}private synchronized void cancelOutstandingTask() {if (outstandingTask != null) {if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) {outstandingTask.cancel(true);}outstandingTask = null;}}synchronized void stop() {stopped = true;
// if (useAutoFocus) {cancelOutstandingTask();// Doesn't hurt to call this even if not focusingtry {camera.cancelAutoFocus();} catch (RuntimeException re) {// Have heard RuntimeException reported in Android 4.0.x+; continue?Log.w(TAG, "Unexpected exception while cancelling focusing", re);}
// }}private final class AutoFocusTask extends AsyncTask<Object,Object,Object> {@Overrideprotected Object doInBackground(Object... voids) {try {Thread.sleep(AUTO_FOCUS_INTERVAL_MS);} catch (InterruptedException e) {// continue}start();return null;}}}
CameraConfigurationManager.java
final class CameraConfigurationManager {private static final String TAG = "CameraConfiguration";private final Context context;private int cwNeededRotation;private int cwRotationFromDisplayToCamera;private Point screenResolution;private Point cameraResolution;private Point bestPreviewSize;private Point previewSizeOnScreen;CameraConfigurationManager(Context context) {this.context = context;}/*** Reads, one time, values from the camera that are needed by the app.*/void initFromCameraParameters(OpenCamera camera) {Camera.Parameters parameters = camera.getCamera().getParameters();WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);Display display = manager.getDefaultDisplay();int displayRotation = display.getRotation();int cwRotationFromNaturalToDisplay;switch (displayRotation) {case Surface.ROTATION_0:cwRotationFromNaturalToDisplay = 0;break;case Surface.ROTATION_90:cwRotationFromNaturalToDisplay = 90;break;case Surface.ROTATION_180:cwRotationFromNaturalToDisplay = 180;break;case Surface.ROTATION_270:cwRotationFromNaturalToDisplay = 270;break;default:// Have seen this return incorrect values like -90if (displayRotation % 90 == 0) {cwRotationFromNaturalToDisplay = (360 + displayRotation) % 360;} else {throw new IllegalArgumentException("Bad rotation: " + displayRotation);}}Log.i(TAG, "Display at: " + cwRotationFromNaturalToDisplay);int cwRotationFromNaturalToCamera = camera.getOrientation();Log.i(TAG, "Camera at: " + cwRotationFromNaturalToCamera);// Still not 100% sure about this. But acts like we need to flip this:if (camera.getFacing() == CameraFacing.FRONT) {cwRotationFromNaturalToCamera = (360 - cwRotationFromNaturalToCamera) % 360;Log.i(TAG, "Front camera overriden to: " + cwRotationFromNaturalToCamera);}/*SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);String overrideRotationString;if (camera.getFacing() == CameraFacing.FRONT) {overrideRotationString = prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION_FRONT, null);} else {overrideRotationString = prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION, null);}if (overrideRotationString != null && !"-".equals(overrideRotationString)) {Log.i(TAG, "Overriding camera manually to " + overrideRotationString);cwRotationFromNaturalToCamera = Integer.parseInt(overrideRotationString);}*/cwRotationFromDisplayToCamera =(360 + cwRotationFromNaturalToCamera - cwRotationFromNaturalToDisplay) % 360;Log.i(TAG, "Final display orientation: " + cwRotationFromDisplayToCamera);if (camera.getFacing() == CameraFacing.FRONT) {Log.i(TAG, "Compensating rotation for front camera");cwNeededRotation = (360 - cwRotationFromDisplayToCamera) % 360;} else {cwNeededRotation = cwRotationFromDisplayToCamera;}Log.i(TAG, "Clockwise rotation from display to camera: " + cwNeededRotation);Point theScreenResolution = new Point();display.getSize(theScreenResolution);screenResolution = theScreenResolution;Log.i(TAG, "Screen resolution in current orientation: " + screenResolution);cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);Log.i(TAG, "Camera resolution: " + cameraResolution);bestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);Log.i(TAG, "Best available preview size: " + bestPreviewSize);boolean isScreenPortrait = screenResolution.x < screenResolution.y;boolean isPreviewSizePortrait = bestPreviewSize.x < bestPreviewSize.y;if (isScreenPortrait == isPreviewSizePortrait) {previewSizeOnScreen = bestPreviewSize;} else {previewSizeOnScreen = new Point(bestPreviewSize.y, bestPreviewSize.x);}Log.i(TAG, "Preview size on screen: " + previewSizeOnScreen);}void setDesiredCameraParameters(OpenCamera camera, boolean safeMode) {Camera theCamera = camera.getCamera();Camera.Parameters parameters = theCamera.getParameters();if (parameters == null) {Log.w(TAG, "Device error: no camera parameters are available. Proceeding without configuration.");return;}Log.i(TAG, "Initial camera parameters: " + parameters.flatten());if (safeMode) {Log.w(TAG, "In camera config safe mode -- most settings will not be honored");}// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);initializeTorch(parameters, safeMode, QRConstants.disableExposure);CameraConfigurationUtils.setFocus(parameters,//是否支持自动对焦QRConstants.autoFocus,true,safeMode);if (!safeMode) {////CameraConfigurationUtils.setInvertColor(parameters);CameraConfigurationUtils.setBarcodeSceneMode(parameters);CameraConfigurationUtils.setVideoStabilization(parameters);CameraConfigurationUtils.setFocusArea(parameters);CameraConfigurationUtils.setMetering(parameters);}parameters.setPreviewSize(bestPreviewSize.x, bestPreviewSize.y);theCamera.setParameters(parameters);theCamera.setDisplayOrientation(cwRotationFromDisplayToCamera);Camera.Parameters afterParameters = theCamera.getParameters();Camera.Size afterSize = afterParameters.getPreviewSize();if (afterSize != null && (bestPreviewSize.x != afterSize.width || bestPreviewSize.y != afterSize.height)) {Log.w(TAG, "Camera said it supported preview size " + bestPreviewSize.x + 'x' + bestPreviewSize.y +", but after setting it, preview size is " + afterSize.width + 'x' + afterSize.height);bestPreviewSize.x = afterSize.width;bestPreviewSize.y = afterSize.height;}}Point getBestPreviewSize() {return bestPreviewSize;}Point getPreviewSizeOnScreen() {return previewSizeOnScreen;}Point getCameraResolution() {return cameraResolution;}Point getScreenResolution() {return screenResolution;}int getCWNeededRotation() {return cwNeededRotation;}boolean getTorchState(Camera camera) {if (camera != null) {Camera.Parameters parameters = camera.getParameters();if (parameters != null) {String flashMode = camera.getParameters().getFlashMode();return flashMode != null &&(Camera.Parameters.FLASH_MODE_ON.equals(flashMode) ||Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode));}}return false;}void setTorch(Camera camera, boolean newSetting) {Camera.Parameters parameters = camera.getParameters();doSetTorch(parameters, newSetting, false, QRConstants.disableExposure);camera.setParameters(parameters);}private void initializeTorch(Camera.Parameters parameters, boolean safeMode, boolean disableExposure) {boolean currentSetting = QRConstants.frontLightMode == FrontLightMode.ON;doSetTorch(parameters, currentSetting, safeMode, disableExposure);}private void doSetTorch(Camera.Parameters parameters, boolean newSetting, boolean safeMode, boolean disableExposure) {CameraConfigurationUtils.setTorch(parameters, newSetting);if (!safeMode && !disableExposure) {CameraConfigurationUtils.setBestExposure(parameters, newSetting);}}}
CameraConfigurationUtils.java
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
public final class CameraConfigurationUtils {private static final String TAG = "CameraConfiguration";private static final Pattern SEMICOLON = Pattern.compile(";");private static final int MIN_PREVIEW_PIXELS = 480 * 320; // normal screenprivate static final float MAX_EXPOSURE_COMPENSATION = 1.5f;private static final float MIN_EXPOSURE_COMPENSATION = 0.0f;private static final double MAX_ASPECT_DISTORTION = 0.15;private static final int MIN_FPS = 10;private static final int MAX_FPS = 20;private static final int AREA_PER_1000 = 400;private CameraConfigurationUtils() {}public static void setFocus(Camera.Parameters parameters,boolean autoFocus,boolean disableContinuous,boolean safeMode) {List<String> supportedFocusModes = parameters.getSupportedFocusModes();String focusMode = null;if (autoFocus) {if (safeMode || disableContinuous) {focusMode = findSettableValue("focus mode",supportedFocusModes,Camera.Parameters.FOCUS_MODE_AUTO);} else {focusMode = findSettableValue("focus mode",supportedFocusModes,Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,Camera.Parameters.FOCUS_MODE_AUTO);}}// Maybe selected auto-focus but not available, so fall through here:if (!safeMode && focusMode == null) {focusMode = findSettableValue("focus mode",supportedFocusModes,Camera.Parameters.FOCUS_MODE_MACRO,Camera.Parameters.FOCUS_MODE_EDOF);}if (focusMode != null) {if (focusMode.equals(parameters.getFocusMode())) {Log.i(TAG, "Focus mode already set to " + focusMode);} else {parameters.setFocusMode(focusMode);}}}public static void setTorch(Camera.Parameters parameters, boolean on) {List<String> supportedFlashModes = parameters.getSupportedFlashModes();String flashMode;if (on) {flashMode = findSettableValue("flash mode",supportedFlashModes,Camera.Parameters.FLASH_MODE_TORCH,Camera.Parameters.FLASH_MODE_ON);} else {flashMode = findSettableValue("flash mode",supportedFlashModes,Camera.Parameters.FLASH_MODE_OFF);}if (flashMode != null) {if (flashMode.equals(parameters.getFlashMode())) {Log.i(TAG, "Flash mode already set to " + flashMode);} else {Log.i(TAG, "Setting flash mode to " + flashMode);parameters.setFlashMode(flashMode);}}}public static void setBestExposure(Camera.Parameters parameters, boolean lightOn) {int minExposure = parameters.getMinExposureCompensation();int maxExposure = parameters.getMaxExposureCompensation();float step = parameters.getExposureCompensationStep();if ((minExposure != 0 || maxExposure != 0) && step > 0.0f) {// Set low when light is onfloat targetCompensation = lightOn ? MIN_EXPOSURE_COMPENSATION : MAX_EXPOSURE_COMPENSATION;int compensationSteps = Math.round(targetCompensation / step);float actualCompensation = step * compensationSteps;// Clamp value:compensationSteps = Math.max(Math.min(compensationSteps, maxExposure), minExposure);if (parameters.getExposureCompensation() == compensationSteps) {Log.i(TAG, "Exposure compensation already set to " + compensationSteps + " / " + actualCompensation);} else {Log.i(TAG, "Setting exposure compensation to " + compensationSteps + " / " + actualCompensation);parameters.setExposureCompensation(compensationSteps);}} else {Log.i(TAG, "Camera does not support exposure compensation");}}public static void setBestPreviewFPS(Camera.Parameters parameters) {setBestPreviewFPS(parameters, MIN_FPS, MAX_FPS);}public static void setBestPreviewFPS(Camera.Parameters parameters, int minFPS, int maxFPS) {List<int[]> supportedPreviewFpsRanges = parameters.getSupportedPreviewFpsRange();Log.i(TAG, "Supported FPS ranges: " + toString(supportedPreviewFpsRanges));if (supportedPreviewFpsRanges != null && !supportedPreviewFpsRanges.isEmpty()) {int[] suitableFPSRange = null;for (int[] fpsRange : supportedPreviewFpsRanges) {int thisMin = fpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];int thisMax = fpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];if (thisMin >= minFPS * 1000 && thisMax <= maxFPS * 1000) {suitableFPSRange = fpsRange;break;}}if (suitableFPSRange == null) {Log.i(TAG, "No suitable FPS range?");} else {int[] currentFpsRange = new int[2];parameters.getPreviewFpsRange(currentFpsRange);if (Arrays.equals(currentFpsRange, suitableFPSRange)) {Log.i(TAG, "FPS range already set to " + Arrays.toString(suitableFPSRange));} else {Log.i(TAG, "Setting FPS range to " + Arrays.toString(suitableFPSRange));parameters.setPreviewFpsRange(suitableFPSRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],suitableFPSRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);}}}}public static void setFocusArea(Camera.Parameters parameters) {if (parameters.getMaxNumFocusAreas() > 0) {Log.i(TAG, "Old focus areas: " + toString(parameters.getFocusAreas()));List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);Log.i(TAG, "Setting focus area to : " + toString(middleArea));parameters.setFocusAreas(middleArea);} else {Log.i(TAG, "Device does not support focus areas");}}public static void setMetering(Camera.Parameters parameters) {if (parameters.getMaxNumMeteringAreas() > 0) {Log.i(TAG, "Old metering areas: " + parameters.getMeteringAreas());List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);Log.i(TAG, "Setting metering area to : " + toString(middleArea));parameters.setMeteringAreas(middleArea);} else {Log.i(TAG, "Device does not support metering areas");}}private static List<Camera.Area> buildMiddleArea(int areaPer1000) {return Collections.singletonList(new Camera.Area(new Rect(-areaPer1000, -areaPer1000, areaPer1000, areaPer1000), 1));}public static void setVideoStabilization(Camera.Parameters parameters) {if (parameters.isVideoStabilizationSupported()) {if (parameters.getVideoStabilization()) {Log.i(TAG, "Video stabilization already enabled");} else {Log.i(TAG, "Enabling video stabilization...");parameters.setVideoStabilization(true);}} else {Log.i(TAG, "This device does not support video stabilization");}}public static void setBarcodeSceneMode(Camera.Parameters parameters) {if (Camera.Parameters.SCENE_MODE_BARCODE.equals(parameters.getSceneMode())) {Log.i(TAG, "Barcode scene mode already set");return;}String sceneMode = findSettableValue("scene mode",parameters.getSupportedSceneModes(),Camera.Parameters.SCENE_MODE_BARCODE);if (sceneMode != null) {parameters.setSceneMode(sceneMode);}}public static void setZoom(Camera.Parameters parameters, double targetZoomRatio) {if (parameters.isZoomSupported()) {Integer zoom = indexOfClosestZoom(parameters, targetZoomRatio);if (zoom == null) {return;}if (parameters.getZoom() == zoom) {Log.i(TAG, "Zoom is already set to " + zoom);} else {Log.i(TAG, "Setting zoom to " + zoom);parameters.setZoom(zoom);}} else {Log.i(TAG, "Zoom is not supported");}}private static Integer indexOfClosestZoom(Camera.Parameters parameters, double targetZoomRatio) {List<Integer> ratios = parameters.getZoomRatios();Log.i(TAG, "Zoom ratios: " + ratios);int maxZoom = parameters.getMaxZoom();if (ratios == null || ratios.isEmpty() || ratios.size() != maxZoom + 1) {Log.w(TAG, "Invalid zoom ratios!");return null;}double target100 = 100.0 * targetZoomRatio;double smallestDiff = Double.POSITIVE_INFINITY;int closestIndex = 0;for (int i = 0; i < ratios.size(); i++) {double diff = Math.abs(ratios.get(i) - target100);if (diff < smallestDiff) {smallestDiff = diff;closestIndex = i;}}Log.i(TAG, "Chose zoom ratio of " + (ratios.get(closestIndex) / 100.0));return closestIndex;}public static void setInvertColor(Camera.Parameters parameters) {if (Camera.Parameters.EFFECT_NEGATIVE.equals(parameters.getColorEffect())) {Log.i(TAG, "Negative effect already set");return;}String colorMode = findSettableValue("color effect",parameters.getSupportedColorEffects(),Camera.Parameters.EFFECT_NEGATIVE);if (colorMode != null) {parameters.setColorEffect(colorMode);}}public static Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();if (rawSupportedSizes == null) {Log.w(TAG, "Device returned no supported preview sizes; using default");Camera.Size defaultSize = parameters.getPreviewSize();if (defaultSize == null) {throw new IllegalStateException("Parameters contained no preview size!");}return new Point(defaultSize.width, defaultSize.height);}// Sort by size, descendingList<Camera.Size> supportedPreviewSizes = new ArrayList<>(rawSupportedSizes);Collections.sort(supportedPreviewSizes, new Comparator<Camera.Size>() {@Overridepublic int compare(Camera.Size a, Camera.Size b) {int aPixels = a.height * a.width;int bPixels = b.height * b.width;if (bPixels < aPixels) {return -1;}if (bPixels > aPixels) {return 1;}return 0;}});if (Log.isLoggable(TAG, Log.INFO)) {StringBuilder previewSizesString = new StringBuilder();for (Camera.Size supportedPreviewSize : supportedPreviewSizes) {previewSizesString.append(supportedPreviewSize.width).append('x').append(supportedPreviewSize.height).append(' ');}Log.i(TAG, "Supported preview sizes: " + previewSizesString);}double screenAspectRatio = (double) screenResolution.x / (double) screenResolution.y;// Remove sizes that are unsuitableIterator<Camera.Size> it = supportedPreviewSizes.iterator();while (it.hasNext()) {Camera.Size supportedPreviewSize = it.next();int realWidth = supportedPreviewSize.width;int realHeight = supportedPreviewSize.height;if (realWidth * realHeight < MIN_PREVIEW_PIXELS) {it.remove();continue;}boolean isCandidatePortrait = realWidth < realHeight;int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;double distortion = Math.abs(aspectRatio - screenAspectRatio);if (distortion > MAX_ASPECT_DISTORTION) {it.remove();continue;}if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {Point exactPoint = new Point(realWidth, realHeight);Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint);return exactPoint;}}// If no exact match, use largest preview size. This was not a great idea on older devices because// of the additional computation needed. We're likely to get here on newer Android 4+ devices, where// the CPU is much more powerful.if (!supportedPreviewSizes.isEmpty()) {Camera.Size largestPreview = supportedPreviewSizes.get(0);Point largestSize = new Point(largestPreview.width, largestPreview.height);Log.i(TAG, "Using largest suitable preview size: " + largestSize);return largestSize;}// If there is nothing at all suitable, return current preview sizeCamera.Size defaultPreview = parameters.getPreviewSize();if (defaultPreview == null) {throw new IllegalStateException("Parameters contained no preview size!");}Point defaultSize = new Point(defaultPreview.width, defaultPreview.height);Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize);return defaultSize;}private static String findSettableValue(String name,Collection<String> supportedValues,String... desiredValues) {Log.i(TAG, "Requesting " + name + " value from among: " + Arrays.toString(desiredValues));Log.i(TAG, "Supported " + name + " values: " + supportedValues);if (supportedValues != null) {for (String desiredValue : desiredValues) {if (supportedValues.contains(desiredValue)) {Log.i(TAG, "Can set " + name + " to: " + desiredValue);return desiredValue;}}}Log.i(TAG, "No supported values match");return null;}private static String toString(Collection<int[]> arrays) {if (arrays == null || arrays.isEmpty()) {return "[]";}StringBuilder buffer = new StringBuilder();buffer.append('[');Iterator<int[]> it = arrays.iterator();while (it.hasNext()) {buffer.append(Arrays.toString(it.next()));if (it.hasNext()) {buffer.append(", ");}}buffer.append(']');return buffer.toString();}private static String toString(Iterable<Camera.Area> areas) {if (areas == null) {return null;}StringBuilder result = new StringBuilder();for (Camera.Area area : areas) {result.append(area.rect).append(':').append(area.weight).append(' ');}return result.toString();}public static String collectStats(Camera.Parameters parameters) {return collectStats(parameters.flatten());}public static String collectStats(CharSequence flattenedParams) {StringBuilder result = new StringBuilder(1000);result.append("BOARD=").append(Build.BOARD).append('\n');result.append("BRAND=").append(Build.BRAND).append('\n');result.append("CPU_ABI=").append(Build.CPU_ABI).append('\n');result.append("DEVICE=").append(Build.DEVICE).append('\n');result.append("DISPLAY=").append(Build.DISPLAY).append('\n');result.append("FINGERPRINT=").append(Build.FINGERPRINT).append('\n');result.append("HOST=").append(Build.HOST).append('\n');result.append("ID=").append(Build.ID).append('\n');result.append("MANUFACTURER=").append(Build.MANUFACTURER).append('\n');result.append("MODEL=").append(Build.MODEL).append('\n');result.append("PRODUCT=").append(Build.PRODUCT).append('\n');result.append("TAGS=").append(Build.TAGS).append('\n');result.append("TIME=").append(Build.TIME).append('\n');result.append("TYPE=").append(Build.TYPE).append('\n');result.append("USER=").append(Build.USER).append('\n');result.append("VERSION.CODENAME=").append(Build.VERSION.CODENAME).append('\n');result.append("VERSION.INCREMENTAL=").append(Build.VERSION.INCREMENTAL).append('\n');result.append("VERSION.RELEASE=").append(Build.VERSION.RELEASE).append('\n');result.append("VERSION.SDK_INT=").append(Build.VERSION.SDK_INT).append('\n');if (flattenedParams != null) {String[] params = SEMICOLON.split(flattenedParams);Arrays.sort(params);for (String param : params) {result.append(param).append('\n');}}return result.toString();}}
CameraManager.java
public final class CameraManager {private static final String TAG = CameraManager.class.getSimpleName();private static final int MIN_FRAME_WIDTH = 240;private static final int MIN_FRAME_HEIGHT = 240;private static final int MAX_FRAME_WIDTH = 1200; // = 5/8 * 1920private static final int MAX_FRAME_HEIGHT = 675; // = 5/8 * 1080private final Context context;private final CameraConfigurationManager configManager;private OpenCamera camera;private AutoFocusManager autoFocusManager;private Rect framingRect;private Rect framingRectInPreview;private boolean initialized;private boolean previewing;private int requestedCameraId = OpenCameraInterface.NO_REQUESTED_CAMERA;private int requestedFramingRectWidth;private int requestedFramingRectHeight;/*** Preview frames are delivered here, which we pass on to the registered handler. Make sure to* clear the handler so it will only receive one message.*/private final PreviewCallback previewCallback;public CameraManager(Context context) {this.context = context;this.configManager = new CameraConfigurationManager(context);previewCallback = new PreviewCallback(configManager);}/*** Opens the camera driver and initializes the hardware parameters.** @param holder The surface object which the camera will draw preview frames into.* @throws IOException Indicates the camera driver failed to open.*/public synchronized void openDriver(SurfaceHolder holder) throws IOException {OpenCamera theCamera = camera;if (theCamera == null) {theCamera = OpenCameraInterface.open(requestedCameraId);if (theCamera == null) {throw new IOException("Camera.open() failed to return object from driver");}camera = theCamera;}if (!initialized) {initialized = true;configManager.initFromCameraParameters(theCamera);if (requestedFramingRectWidth > 0 && requestedFramingRectHeight > 0) {setManualFramingRect(requestedFramingRectWidth, requestedFramingRectHeight);requestedFramingRectWidth = 0;requestedFramingRectHeight = 0;}}Camera cameraObject = theCamera.getCamera();Camera.Parameters parameters = cameraObject.getParameters();String parametersFlattened = parameters == null ? null : parameters.flatten(); // Save these, temporarilytry {configManager.setDesiredCameraParameters(theCamera, false);} catch (RuntimeException re) {// Driver failedLog.w(TAG, "Camera rejected parameters. Setting only minimal safe-mode parameters");Log.i(TAG, "Resetting to saved camera params: " + parametersFlattened);// Reset:if (parametersFlattened != null) {parameters = cameraObject.getParameters();parameters.unflatten(parametersFlattened);try {cameraObject.setParameters(parameters);configManager.setDesiredCameraParameters(theCamera, true);} catch (RuntimeException re2) {// Well, darn. Give upLog.w(TAG, "Camera rejected even safe-mode parameters! No configuration");}}}cameraObject.setPreviewDisplay(holder);}public synchronized boolean isOpen() {return camera != null;}/*** Closes the camera driver if still in use.*/public synchronized void closeDriver() {if (camera != null) {camera.getCamera().release();camera = null;// Make sure to clear these each time we close the camera, so that any scanning rect// requested by intent is forgotten.framingRect = null;framingRectInPreview = null;}}/*** Asks the camera hardware to begin drawing preview frames to the screen.*/public synchronized void startPreview() {OpenCamera theCamera = camera;if (theCamera != null && !previewing) {theCamera.getCamera().startPreview();previewing = true;autoFocusManager = new AutoFocusManager(context, theCamera.getCamera());}}/*** Tells the camera to stop drawing preview frames.*/public synchronized void stopPreview() {if (autoFocusManager != null) {autoFocusManager.stop();autoFocusManager = null;}if (camera != null && previewing) {camera.getCamera().stopPreview();previewCallback.setHandler(null, 0);previewing = false;}}/*** Convenience method for {@link CaptureActivity}** @param newSetting if {@code true}, light should be turned on if currently off. And vice versa.*/public synchronized void setTorch(boolean newSetting) {OpenCamera theCamera = camera;if (theCamera != null) {if (newSetting != configManager.getTorchState(theCamera.getCamera())) {boolean wasAutoFocusManager = autoFocusManager != null;if (wasAutoFocusManager) {autoFocusManager.stop();autoFocusManager = null;}configManager.setTorch(theCamera.getCamera(), newSetting);if (wasAutoFocusManager) {autoFocusManager = new AutoFocusManager(context, theCamera.getCamera());autoFocusManager.start();}}}}/*** A single preview frame will be returned to the handler supplied. The data will arrive as byte[]* in the message.obj field, with width and height encoded as message.arg1 and message.arg2,* respectively.** @param handler The handler to send the message to.* @param message The what field of the message to be sent.*/public synchronized void requestPreviewFrame(Handler handler, int message) {OpenCamera theCamera = camera;if (theCamera != null && previewing) {previewCallback.setHandler(handler, message);theCamera.getCamera().setOneShotPreviewCallback(previewCallback);}}/*** Calculates the framing rect which the UI should draw to show the user where to place the* barcode. This target helps with alignment as well as forces the user to hold the device* far enough away to ensure the image will be in focus.** @return The rectangle to draw on screen in window coordinates.*/public synchronized Rect getFramingRect() {if (framingRect == null) {if (camera == null) {return null;}Point screenResolution = configManager.getScreenResolution();if (screenResolution == null) {// Called early, before init even finishedreturn null;}int width = findDesiredDimensionInRange(screenResolution.x, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);int height = findDesiredDimensionInRange(screenResolution.y, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);//保持扫描框宽高一致int finalSize = height;if (height > width) {finalSize = width;}int leftOffset = (screenResolution.x - finalSize) / 2;int topOffset = (screenResolution.y - finalSize) / 2;framingRect = new Rect(leftOffset, topOffset, leftOffset + finalSize, topOffset + finalSize);Log.d(TAG, "Calculated framing rect: " + framingRect + " width =" + width + " height = " + height +" screenResolution.x = " + screenResolution.x + " screenResolution.y = " + screenResolution.y);}return framingRect;}private static int findDesiredDimensionInRange(int resolution, int hardMin, int hardMax) {int dim = 5 * resolution / 8; // Target 5/8 of each dimensionif (dim < hardMin) {return hardMin;}if (dim > hardMax) {return hardMax;}return dim;}/*** Like {@link #getFramingRect} but coordinates are in terms of the preview frame,* not UI / screen.** @return {@link Rect} expressing barcode scan area in terms of the preview size*/public synchronized Rect getFramingRectInPreview() {if (framingRectInPreview == null) {Rect framingRect = getFramingRect();if (framingRect == null) {return null;}Rect rect = new Rect(framingRect);Point cameraResolution = configManager.getCameraResolution();Point screenResolution = configManager.getScreenResolution();if (cameraResolution == null || screenResolution == null) {// Called early, before init even finishedreturn null;}rect.left = rect.left * cameraResolution.x / screenResolution.x;rect.right = rect.right * cameraResolution.x / screenResolution.x;rect.top = rect.top * cameraResolution.y / screenResolution.y;rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;framingRectInPreview = rect;}return framingRectInPreview;}/*** Allows third party apps 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 synchronized void setManualCameraId(int cameraId) {requestedCameraId = cameraId;}/*** Allows third party apps to specify the scanning rectangle dimensions, rather than determine* them automatically based on screen resolution.** @param width The width in pixels to scan.* @param height The height in pixels to scan.*/public synchronized void setManualFramingRect(int width, int height) {if (initialized) {Point screenResolution = configManager.getScreenResolution();if (width > screenResolution.x) {width = screenResolution.x;}if (height > screenResolution.y) {height = screenResolution.y;}int leftOffset = (screenResolution.x - width) / 2;int topOffset = (screenResolution.y - height) / 2;framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);Log.d(TAG, "Calculated manual framing rect: " + framingRect);framingRectInPreview = null;} else {requestedFramingRectWidth = width;requestedFramingRectHeight = height;}}/*** A factory method to build the appropriate LuminanceSource object based on the format* of the preview buffers, as described by Camera.Parameters.** @param data A preview frame.* @param width The width of the image.* @param height The height of the image.* @return A PlanarYUVLuminanceSource instance.*/public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {Rect rect = getFramingRectInPreview();if (rect == null) {return null;}// Go ahead and assume it's YUV rather than die.return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,rect.width(), rect.height(), false);}public void openLight() {if (camera != null && camera.getCamera() != null) {Camera.Parameters parameter = camera.getCamera().getParameters();parameter.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);camera.getCamera().setParameters(parameter);}}public void offLight() {if (camera != null && camera.getCamera() != null) {Camera.Parameters parameter = camera.getCamera().getParameters();parameter.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);camera.getCamera().setParameters(parameter);}}}
FrontLightMode.java
public enum FrontLightMode {/** Always on. */ON,/** On only when ambient light is low. */AUTO,/** Always off. */OFF/* private static FrontLightMode parse(String modeString) {return modeString == null ? OFF : valueOf(modeString);}public static FrontLightMode readPref(SharedPreferences sharedPrefs) {return parse(sharedPrefs.getString(PreferencesActivity.KEY_FRONT_LIGHT_MODE, OFF.toString()));}*/}
PreviewCallback.java
final class PreviewCallback implements Camera.PreviewCallback {private static final String TAG = PreviewCallback.class.getSimpleName();private final CameraConfigurationManager configManager;private Handler previewHandler;private int previewMessage;PreviewCallback(CameraConfigurationManager configManager) {this.configManager = configManager;}void setHandler(Handler previewHandler, int previewMessage) {this.previewHandler = previewHandler;this.previewMessage = previewMessage;}@Overridepublic void onPreviewFrame(byte[] data, Camera camera) {Point cameraResolution = configManager.getCameraResolution();Handler thePreviewHandler = previewHandler;if (cameraResolution != null && thePreviewHandler != null) {//给DecodeHandler发消息Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x,cameraResolution.y, data);message.sendToTarget();previewHandler = null;} else {Log.d(TAG, "Got preview callback, but no handler or resolution available");}}}
QRConstants.java
public class QRConstants {public static boolean vibrateEnable = true;public static boolean beepEnable = true;public static FrontLightMode frontLightMode = FrontLightMode.OFF;public static boolean disableExposure = true;public static boolean autoFocus = true;public static final String KEY_AUTO_FOCUS = "preferences_auto_focus";}
Constant.java
public class Constant {public static final int DECODE = 1;public static final int DECODE_FAILED = 2;public static final int DECODE_SUCCEEDED = 3;public static final int LAUNCH_PRODUCT_QUERY = 4;public static final int QUIT = 5;public static final int RESTART_PREVIEW = 6;public static final int RETURN_SCAN_RESULT = 7;public static final int FLASH_OPEN = 8;public static final int FLASH_CLOSE = 9;public static final int REQUEST_IMAGE = 10;public static final String CODED_CONTENT = "codedContent";public static final String CODED_BITMAP = "codedBitmap";/*传递的zxingconfing*/public static final String INTENT_ZXING_CONFIG = "zxingConfig";}
AmbientLightManager.java
public final class AmbientLightManager implements SensorEventListener {private static final float TOO_DARK_LUX = 45.0f;private static final float BRIGHT_ENOUGH_LUX = 450.0f;private final Context context;private CameraManager cameraManager;private Sensor lightSensor;public AmbientLightManager(Context context) {this.context = context;}public void start(CameraManager cameraManager) {this.cameraManager = cameraManager;SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);if (QRConstants.frontLightMode == FrontLightMode.AUTO) {SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);if (lightSensor != null) {sensorManager.registerListener(this, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);}}}public void stop() {if (lightSensor != null) {SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);sensorManager.unregisterListener(this);cameraManager = null;lightSensor = null;}}@Overridepublic void onSensorChanged(SensorEvent sensorEvent) {float ambientLightLux = sensorEvent.values[0];if (cameraManager != null) {if (ambientLightLux <= TOO_DARK_LUX) {cameraManager.setTorch(true);} else if (ambientLightLux >= BRIGHT_ENOUGH_LUX) {cameraManager.setTorch(false);}}}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {// do nothing}}
BeepManager.java
public final class BeepManager implements MediaPlayer.OnCompletionListener,MediaPlayer.OnErrorListener, Closeable {private static final String TAG = BeepManager.class.getSimpleName();private static final float BEEP_VOLUME = 0.10f;private static final long VIBRATE_DURATION = 200L;private final Activity activity;private MediaPlayer mediaPlayer;private boolean playBeep;private boolean vibrate;public BeepManager(Activity activity) {this.activity = activity;this.mediaPlayer = null;updatePrefs();}public boolean isPlayBeep() {return playBeep;}public void setPlayBeep(boolean playBeep) {this.playBeep = playBeep;}public boolean isVibrate() {return vibrate;}public void setVibrate(boolean vibrate) {this.vibrate = vibrate;}public synchronized void updatePrefs() {if (playBeep && mediaPlayer == null) {// The volume on STREAM_SYSTEM is not adjustable, and users found it// too loud,// so we now play on the music stream.// 设置activity音量控制键控制的音频流activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);mediaPlayer = buildMediaPlayer(activity);}}/*** 开启响铃和震动*/@SuppressLint("MissingPermission")public synchronized void playBeepSoundAndVibrate() {if (playBeep && mediaPlayer != null) {mediaPlayer.start();}if (vibrate) {Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);vibrator.vibrate(VIBRATE_DURATION);}}/*** 创建MediaPlayer** @param activity* @return*/private MediaPlayer buildMediaPlayer(Context activity) {MediaPlayer mediaPlayer = new MediaPlayer();mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);// 监听是否播放完成mediaPlayer.setOnCompletionListener(this);mediaPlayer.setOnErrorListener(this);// 配置播放资源try {AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.beep);try {mediaPlayer.setDataSource(file.getFileDescriptor(),file.getStartOffset(), file.getLength());} finally {file.close();}// 设置音量mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);mediaPlayer.prepare();return mediaPlayer;} catch (IOException ioe) {Log.w(TAG, ioe);mediaPlayer.release();return null;}}@Overridepublic void onCompletion(MediaPlayer mp) {// When the beep has finished playing, rewind to queue up another one.mp.seekTo(0);}@Overridepublic synchronized boolean onError(MediaPlayer mp, int what, int extra) {if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {// we are finished, so put up an appropriate error toast if required// and finishactivity.finish();} else {// possibly media player error, so release and recreatemp.release();mediaPlayer = null;updatePrefs();}return true;}@Overridepublic synchronized void close() {if (mediaPlayer != null) {mediaPlayer.release();mediaPlayer = null;}}}
CaptureActivity.java
public class CaptureActivity extends Activity implements SurfaceHolder.Callback, View.OnClickListener {static {AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);//处理api 5.1以下手机不兼容问题}private static final String TAG = CaptureActivity.class.getSimpleName();private ImageView mBackmImg;private TextView mTitle;public int REQ_ID_GALLERY = 0;public static boolean isLightOn = false;private IMResUtil mImResUtil;public static void startAction(Activity activity, Bundle bundle, int requestCode) {Intent intent = new Intent(activity, CaptureActivity.class);intent.putExtras(bundle);activity.startActivityForResult(intent, requestCode);}private CameraManager cameraManager;private CaptureActivityHandler handler;private ViewfinderView viewfinderView;private boolean hasSurface;private Collection<BarcodeFormat> decodeFormats;private String characterSet;private InactivityTimer inactivityTimer;private BeepManager beepManager;private AmbientLightManager ambientLightManager;private LinearLayout bottomLayout;private TextView flashLightTv;private ImageView flashLightIv;private LinearLayout flashLightLayout;private LinearLayout albumLayout;private ZxingConfig config;private ImageView img_phone;public ViewfinderView getViewfinderView() {return viewfinderView;}public Handler getHandler() {return handler;}public CameraManager getCameraManager() {return cameraManager;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mImResUtil = new IMResUtil(this);setContentView(mImResUtil.getLayout("activity_device_qrcode_capture"));//设置浸入式状态栏setColor(this, Color.BLACK);hasSurface = false;inactivityTimer = new InactivityTimer(this);ambientLightManager = new AmbientLightManager(this);/*先获取配置信息*/try {config = (ZxingConfig) getIntent().getExtras().get(Constant.INTENT_ZXING_CONFIG);} catch (Exception e) {Log.i("config", e.toString());}if (config == null) {config = new ZxingConfig();}beepManager = new BeepManager(this);beepManager.setPlayBeep(config.isPlayBeep());beepManager.setVibrate(config.isShake());initView(getIntent().getExtras());onEvent(getIntent().getExtras());}private void initView(Bundle bundle) {//判断是否为横屏状态if ("landscape".equals(bundle.getString("portraitOrLandscape"))) {setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);}mBackmImg = (ImageView) findViewById(mImResUtil.getId("iv_qr_back"));mTitle = (TextView) findViewById(mImResUtil.getId("tv_qr_title"));viewfinderView = (ViewfinderView) findViewById(mImResUtil.getId("vv_qr_viewfinderView"));flashLightTv = (TextView) findViewById(mImResUtil.getId("flashLightTv"));bottomLayout = (LinearLayout) findViewById(mImResUtil.getId("bottomLayout"));flashLightIv = (ImageView) findViewById(mImResUtil.getId("flashLightIv"));img_phone = (ImageView) findViewById(mImResUtil.getId("img_phone"));flashLightLayout = (LinearLayout) findViewById(mImResUtil.getId("flashLightLayout"));flashLightLayout.setOnClickListener(this);albumLayout = (LinearLayout) findViewById(mImResUtil.getId("albumLayout"));albumLayout.setOnClickListener(this);}private void onEvent(Bundle bundle) {switchVisibility(bottomLayout, config.isShowbottomLayout());switchVisibility(flashLightLayout, config.isShowFlashLight());switchVisibility(albumLayout, config.isShowAlbum());flashLightIv.setImageResource(R.drawable.device_qrcode_scan_flash_off);img_phone.setImageResource(R.drawable.ic_photo);/*有闪光灯就显示手电筒按钮 否则不显示*/if (isSupportCameraLedFlash(getPackageManager())) {flashLightLayout.setVisibility(View.VISIBLE);} else {flashLightLayout.setVisibility(View.GONE);}/********************新增 END*****************************/mBackmImg.setOnClickListener(this);if (bundle == null) {return;}String titileText = bundle.getString("titileText");if (titileText != null && !titileText.isEmpty()) {mTitle.setText(titileText);}String headColor = bundle.getString("headColor");mTitle.setTextColor(Color.parseColor(headColor));float headSize = bundle.getFloat("headSize");if (headSize > 0) {mTitle.setTextSize(headSize);}}/*** 沉浸式状态栏** @param activity* @param color*/public static void setColor(Activity activity, int color) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {// 设置状态栏透明activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);// 生成一个状态栏大小的矩形View statusView = createStatusView(activity, color);// 添加 statusView 到布局中ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();decorView.addView(statusView);// 设置根布局的参数ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);rootView.setFitsSystemWindows(true);rootView.setClipToPadding(true);}}/*** 绘制一个和状态栏登高的矩形** @param activity* @param color*/private static View createStatusView(Activity activity, int color) {// 获得状态栏高度int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");int statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId);View statusView = new View(activity);LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight);statusView.setLayoutParams(params);statusView.setBackgroundColor(color);return statusView;}@Overridepublic void onClick(View v) {int id = v.getId();if (id == mImResUtil.getId("iv_qr_back")) {this.finish();} else if (id == mImResUtil.getId("albumLayout")) {//打开相册// Intent intent = new Intent();// intent.setType("image/*");// intent.setAction(Intent.ACTION_GET_CONTENT);// intent.addCategory(Intent.CATEGORY_OPENABLE);// startActivityForResult(intent, REQ_ID_GALLERY);/*打开相册*/Intent intent = new Intent();intent.setAction(Intent.ACTION_PICK);intent.setType("image/*");startActivityForResult(intent, REQ_ID_GALLERY);} else if (id == mImResUtil.getId("flashLightLayout")) {//打开手电筒感应部分if (isLightOn) {isLightOn = false;cameraManager.offLight();switchFlashImg(9);} else {isLightOn = true;cameraManager.openLight();switchFlashImg(8);}}}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == REQ_ID_GALLERY) {if (resultCode == RESULT_OK) {Uri uri = data.getData();if (uri != null) {String path = FileUtil.checkPicturePath(CaptureActivity.this, uri);//Device.getActivity()BitmapFactory.Options bmOptions = new BitmapFactory.Options();bmOptions.inJustDecodeBounds = false;bmOptions.inPurgeable = true;Bitmap bmp = BitmapFactory.decodeFile(path, bmOptions);decodeQRCode(bmp, this);}}}//-----------------------------------------------------if (requestCode == Constant.REQUEST_IMAGE && resultCode == RESULT_OK) {String path = ImageUtil.getImageAbsolutePath(this, data.getData());Log.e(TAG, "onActivityResult: -------二维码:path" + path);BitmapFactory.Options bmOptions = new BitmapFactory.Options();bmOptions.inJustDecodeBounds = false;bmOptions.inPurgeable = true;Bitmap bmp = BitmapFactory.decodeFile(path, bmOptions);decodeQRCode(bmp, this);}}/*** 解析二维码图片** @param bitmap 要解析的二维码图片*/public final Map<DecodeHintType, Object> HINTS = new EnumMap<>(DecodeHintType.class);@SuppressLint("StaticFieldLeak")public void decodeQRCode(final Bitmap bitmap, final Activity activity) {new AsyncTask<Void, Void, String>() {@Overrideprotected String doInBackground(Void... params) {try {int width = bitmap.getWidth();int height = bitmap.getHeight();int[] pixels = new int[width * height];bitmap.getPixels(pixels, 0, width, 0, 0, width, height);RGBLuminanceSource source = new RGBLuminanceSource(width, height, pixels);Result result = new MultiFormatReader().decode(new BinaryBitmap(new HybridBinarizer(source)), HINTS);String url = "解析失败";if (result != null && result.getText() != null) {url = result.getText();}Intent resultIntent = new Intent();Bundle bundle = new Bundle();bundle.putString(Constant.CODED_CONTENT, url);resultIntent.putExtras(bundle);activity.setResult(RESULT_OK, resultIntent);CaptureActivity.this.finish();return result.getText();} catch (Exception e) {return null;}}@Overrideprotected void onPostExecute(String result) {Log.d("CaptureActivity", "result=" + result);Toast.makeText(CaptureActivity.this, "解析失败,换个图片试一下", Toast.LENGTH_LONG).show();}}.execute();}@Overrideprotected void onResume() {super.onResume();cameraManager = new CameraManager(getApplication());viewfinderView.setCameraManager(cameraManager);handler = null;beepManager.updatePrefs();ambientLightManager.start(cameraManager);inactivityTimer.onResume();decodeFormats = null;characterSet = null;SurfaceView surfaceView = (SurfaceView) findViewById(mImResUtil.getId("device_qrcode_preview_view"));SurfaceHolder surfaceHolder = surfaceView.getHolder();if (hasSurface) {initCamera(surfaceHolder);} else {surfaceHolder.addCallback(this);}}@Overrideprotected void onPause() {if (handler != null) {handler.quitSynchronously();handler = null;}inactivityTimer.onPause();ambientLightManager.stop();beepManager.close();cameraManager.closeDriver();if (!hasSurface) {SurfaceView surfaceView = (SurfaceView) findViewById(mImResUtil.getId("device_qrcode_preview_view"));SurfaceHolder surfaceHolder = surfaceView.getHolder();surfaceHolder.removeCallback(this);}super.onPause();}@Overrideprotected void onDestroy() {inactivityTimer.shutdown();super.onDestroy();}private void initCamera(SurfaceHolder surfaceHolder) {if (surfaceHolder == null) {throw new IllegalStateException("No SurfaceHolder provided");}if (cameraManager.isOpen()) {Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");return;}try {cameraManager.openDriver(surfaceHolder);// Creating the handler starts the preview, which can also throw a RuntimeException.if (handler == null) {handler = new CaptureActivityHandler(this, decodeFormats, characterSet, cameraManager);}} catch (IOException ioe) {Log.w(TAG, ioe);} catch (RuntimeException e) {// Barcode Scanner has seen crashes in the wild of this variety:// java.?lang.?RuntimeException: Fail to connect to camera serviceLog.e(TAG, "Unexpected error initializing camera", e);}}public void drawViewfinder() {viewfinderView.drawViewfinder();}public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {Log.d("wxl", "rawResult=" + rawResult);boolean fromLiveScan = barcode != null;if (fromLiveScan) {String resultString = rawResult.getText();Log.e("wxl", "rawResult=" + rawResult.getText());beepManager.playBeepSoundAndVibrate();Intent resultIntent = new Intent();Bundle bundle = new Bundle();bundle.putString(Constant.CODED_CONTENT, resultString);resultIntent.putExtras(bundle);this.setResult(RESULT_OK, resultIntent);} else {// this.setResult(RESULT_OK, resultIntent);Toast.makeText(CaptureActivity.this, "扫描失败", Toast.LENGTH_SHORT).show();}CaptureActivity.this.finish();}@Overridepublic void surfaceCreated(SurfaceHolder holder) {if (holder == null) {Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");}if (!hasSurface) {hasSurface = true;initCamera(holder);}}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {hasSurface = false;}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}/********************新增代码 start: **************************/private void switchVisibility(View view, boolean b) {if (b) {view.setVisibility(View.VISIBLE);} else {view.setVisibility(View.GONE);}}/*** @param pm** @return 是否有闪光灯*/public static boolean isSupportCameraLedFlash(PackageManager pm) {if (pm != null) {FeatureInfo[] features = pm.getSystemAvailableFeatures();if (features != null) {for (FeatureInfo f : features) {if (f != null && PackageManager.FEATURE_CAMERA_FLASH.equals(f.name)) {return true;}}}}return false;}/*** @param flashState 切换闪光灯图片*/public void switchFlashImg(int flashState) {if (flashState == Constant.FLASH_OPEN) {flashLightIv.setImageResource(R.drawable.device_qrcode_scan_flash_on);//ic_openflashLightTv.setText("关闭闪光灯");} else {flashLightIv.setImageResource(R.drawable.device_qrcode_scan_flash_off);//ic_closeflashLightTv.setText("打开闪光灯");}}/********************新增代码 END*****************************/
}
CaptureActivityHandler.java
public final class CaptureActivityHandler extends Handler {private static final String TAG = CaptureActivityHandler.class.getSimpleName();private final CaptureActivity activity;private final DecodeThread decodeThread;private State state;private final CameraManager cameraManager;private final IMResUtil mImResUtil;
// private RelativeLayout mLightLinearLay;//LinearLayout 手电筒的线性布局private enum State {PREVIEW, SUCCESS, DONE}public CaptureActivityHandler(CaptureActivity activity, Collection<BarcodeFormat> decodeFormats, String characterSet, CameraManager cameraManager) {this.activity = activity;mImResUtil = new IMResUtil(activity);decodeThread = new DecodeThread(activity, decodeFormats, characterSet, new ViewfinderResultPointCallback(activity.getViewfinderView()));decodeThread.start();state = State.SUCCESS;// Start ourselves capturing previews and decoding.this.cameraManager = cameraManager;cameraManager.startPreview();Log.d(TAG, "CaptureActivityHandler " + CaptureActivityHandler.class.toString());restartPreviewAndDecode();}@Overridepublic void handleMessage(Message message) {int what = message.what;if (what == mImResUtil.getId("device_qrcode_restart_preview")) {restartPreviewAndDecode();} else if (what == mImResUtil.getId("device_qrcode_decode_succeeded")) {state = State.SUCCESS;Bundle bundle = message.getData();Bitmap barcode = null;float scaleFactor = 1.0f;if (bundle != null) {byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);if (compressedBitmap != null) {barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null);// Mutable copy:barcode = barcode.copy(Bitmap.Config.ARGB_8888, true);}scaleFactor = bundle.getFloat(DecodeThread.BARCODE_SCALED_FACTOR);}activity.handleDecode((Result) message.obj, barcode, scaleFactor);} else if (what == mImResUtil.getId("device_qrcode_decode_failed")) {state = State.PREVIEW;cameraManager.requestPreviewFrame(decodeThread.getHandler(), mImResUtil.getId("device_qrcode_decode"));} else if (what == mImResUtil.getId("device_qrcode_return_scan_result")) {activity.setResult(Activity.RESULT_OK, (Intent) message.obj);activity.finish();} else if (what == mImResUtil.getId("device_qrcode_launch_product_query")) {String url = (String) message.obj;Intent intent = new Intent(Intent.ACTION_VIEW);intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);intent.setData(Uri.parse(url));ResolveInfo resolveInfo = activity.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);String browserPackageName = null;if (resolveInfo != null && resolveInfo.activityInfo != null) {browserPackageName = resolveInfo.activityInfo.packageName;Log.d(TAG, "Using browser in package " + browserPackageName);}// Needed for default Android browser / Chrome only apparentlyif ("com.android.browser".equals(browserPackageName) || "com.android.chrome".equals(browserPackageName)) {intent.setPackage(browserPackageName);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.putExtra(Browser.EXTRA_APPLICATION_ID, browserPackageName);}try {activity.startActivity(intent);} catch (ActivityNotFoundException ignored) {Log.w(TAG, "Can't find anything to handle VIEW of URI " + url);}}}public void quitSynchronously() {state = State.DONE;cameraManager.stopPreview();Message quit = Message.obtain(decodeThread.getHandler(), mImResUtil.getId("device_qrcode_quit"));quit.sendToTarget();try {// Wait at most half a second; should be enough time, and onPause() will timeout quicklydecodeThread.join(500L);} catch (InterruptedException e) {// continue}// Be absolutely sure we don't send any queued up messagesremoveMessages(mImResUtil.getId("device_qrcode_decode_succeeded"));removeMessages(mImResUtil.getId("device_qrcode_decode_failed"));}private void restartPreviewAndDecode() {if (state == State.SUCCESS) {state = State.PREVIEW;//decodeThread.getHandler()拿到DecodeHandlercameraManager.requestPreviewFrame(decodeThread.getHandler(), mImResUtil.getId("device_qrcode_decode"));activity.drawViewfinder();}}}
DecodeFormatManager.java
final class DecodeFormatManager {private static final Pattern COMMA_PATTERN = Pattern.compile(",");// static final Set<BarcodeFormat> PRODUCT_FORMATS;
// static final Set<BarcodeFormat> INDUSTRIAL_FORMATS;
// private static final Set<BarcodeFormat> ONE_D_FORMATS;static final Set<BarcodeFormat> QR_CODE_FORMATS = EnumSet.of(BarcodeFormat.QR_CODE);
// static final Set<BarcodeFormat> DATA_MATRIX_FORMATS = EnumSet.of(BarcodeFormat.DATA_MATRIX);
// static final Set<BarcodeFormat> AZTEC_FORMATS = EnumSet.of(BarcodeFormat.AZTEC);
// static final Set<BarcodeFormat> PDF417_FORMATS = EnumSet.of(BarcodeFormat.PDF_417);/*static {PRODUCT_FORMATS = EnumSet.of(BarcodeFormat.UPC_A,BarcodeFormat.UPC_E,BarcodeFormat.EAN_13,BarcodeFormat.EAN_8,BarcodeFormat.RSS_14,BarcodeFormat.RSS_EXPANDED);INDUSTRIAL_FORMATS = EnumSet.of(BarcodeFormat.CODE_39,BarcodeFormat.CODE_93,BarcodeFormat.CODE_128,BarcodeFormat.ITF,BarcodeFormat.CODABAR);ONE_D_FORMATS = EnumSet.copyOf(PRODUCT_FORMATS);ONE_D_FORMATS.addAll(INDUSTRIAL_FORMATS);}*/private static final Map<String,Set<BarcodeFormat>> FORMATS_FOR_MODE;static {FORMATS_FOR_MODE = new HashMap<>();
// FORMATS_FOR_MODE.put(Intents.Scan.ONE_D_MODE, ONE_D_FORMATS);
// FORMATS_FOR_MODE.put(Intents.Scan.PRODUCT_MODE, PRODUCT_FORMATS);FORMATS_FOR_MODE.put(Intents.Scan.QR_CODE_MODE, QR_CODE_FORMATS);
// FORMATS_FOR_MODE.put(Intents.Scan.DATA_MATRIX_MODE, DATA_MATRIX_FORMATS);
// FORMATS_FOR_MODE.put(Intents.Scan.AZTEC_MODE, AZTEC_FORMATS);
// FORMATS_FOR_MODE.put(Intents.Scan.PDF417_MODE, PDF417_FORMATS);}private DecodeFormatManager() {}static Set<BarcodeFormat> parseDecodeFormats(Intent intent) {Iterable<String> scanFormats = null;CharSequence scanFormatsString = intent.getStringExtra(Intents.Scan.FORMATS);if (scanFormatsString != null) {scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));}return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));}static Set<BarcodeFormat> parseDecodeFormats(Uri inputUri) {List<String> formats = inputUri.getQueryParameters(Intents.Scan.FORMATS);if (formats != null && formats.size() == 1 && formats.get(0) != null){formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0)));}return parseDecodeFormats(formats, inputUri.getQueryParameter(Intents.Scan.MODE));}private static Set<BarcodeFormat> parseDecodeFormats(Iterable<String> scanFormats, String decodeMode) {if (scanFormats != null) {Set<BarcodeFormat> formats = EnumSet.noneOf(BarcodeFormat.class);try {for (String format : scanFormats) {formats.add(BarcodeFormat.valueOf(format));}return formats;} catch (IllegalArgumentException iae) {// ignore it then}}if (decodeMode != null) {return FORMATS_FOR_MODE.get(decodeMode);}return null;}}
DecodeHandler.java
final class DecodeHandler extends Handler {private static final String TAG = DecodeHandler.class.getSimpleName();public static boolean isWeakLight = false;private final CaptureActivity activity;private final MultiFormatReader multiFormatReader;private boolean running = true;private final IMResUtil mImResUtil;DecodeHandler(CaptureActivity activity, Map<DecodeHintType, Object> hints) {multiFormatReader = new MultiFormatReader();multiFormatReader.setHints(hints);this.activity = activity;mImResUtil = new IMResUtil(activity);}@Overridepublic void handleMessage(Message message) {if (!running) {return;}int what = message.what;if (what == mImResUtil.getId("device_qrcode_decode")) {decode((byte[]) message.obj, message.arg1, message.arg2);} else if (what == mImResUtil.getId("device_qrcode_quit")) {isWeakLight = false;CaptureActivity.isLightOn = false;running = false;Looper.myLooper().quit();}}/*** Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,* reuse the same reader objects from one decode to the next.** @param data The YUV preview frame.* @param width The width of the preview frame.* @param height The height of the preview frame.*/private void decode(byte[] data, int width, int height) {Log.i(TAG, "decode");//弱光检测analysisColor(data,width,height);long start = System.currentTimeMillis();Result rawResult = null;PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);if (source != null) {BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));try {rawResult = multiFormatReader.decodeWithState(bitmap);} catch (ReaderException re) {// continue} finally {multiFormatReader.reset();}}Handler handler = activity.getHandler();//拿到CaptureActivityHandlerLog.d(TAG, "Found handler " + handler);if (rawResult != null) {// Don't log the barcode contents for security.long end = System.currentTimeMillis();Log.d(TAG, "Found barcode in " + (end - start) + " ms");if (handler != null) {//给CaptureActivityHandler发消息Message message = Message.obtain(handler, mImResUtil.getId("device_qrcode_decode_succeeded"), rawResult);Bundle bundle = new Bundle();bundleThumbnail(source, bundle);message.setData(bundle);message.sendToTarget();}} else {if (handler != null) {Message message = Message.obtain(handler, mImResUtil.getId("device_qrcode_decode_failed"));message.sendToTarget();}}}private static void bundleThumbnail(PlanarYUVLuminanceSource source, Bundle bundle) {int[] pixels = source.renderThumbnail();int width = source.getThumbnailWidth();int height = source.getThumbnailHeight();Bitmap bitmap = Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.ARGB_8888);ByteArrayOutputStream out = new ByteArrayOutputStream();bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);bundle.putByteArray(DecodeThread.BARCODE_BITMAP, out.toByteArray());bundle.putFloat(DecodeThread.BARCODE_SCALED_FACTOR, (float) width / source.getWidth());}private int[] decodeYUV420SP(byte[] yuv420sp, int width, int height) {final int frameSize = width * height;int rgb[] = new int[width * height];for (int j = 0, yp = 0; j < height; j++) {int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;for (int i = 0; i < width; i++, yp++) {int y = (0xff & ((int) yuv420sp[yp])) - 16;if (y < 0) y = 0;if ((i & 1) == 0) {v = (0xff & yuv420sp[uvp++]) - 128;u = (0xff & yuv420sp[uvp++]) - 128;}int y1192 = 1192 * y;int r = (y1192 + 1634 * v);int g = (y1192 - 833 * v - 400 * u);int b = (y1192 + 2066 * u);if (r < 0) r = 0;else if (r > 262143) r = 262143;if (g < 0) g = 0;else if (g > 262143) g = 262143;if (b < 0) b = 0;else if (b > 262143) b = 262143;rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) &0xff00) | ((b >> 10) & 0xff);}}return rgb;}private int getAverageColor(Bitmap bitmap) {int redBucket = 0;int greenBucket = 0;int blueBucket = 0;int pixelCount = 0;for (int y = 0; y < bitmap.getHeight(); y++) {for (int x = 0; x < bitmap.getWidth(); x++) {int c = bitmap.getPixel(x, y);pixelCount++;redBucket += Color.red(c);greenBucket += Color.green(c);blueBucket += Color.blue(c);}}int averageColor = Color.rgb(redBucket / pixelCount, greenBucket/ pixelCount, blueBucket / pixelCount);return averageColor;}//分析预览帧中图片的arg 取平均值public void analysisColor(byte[] data, int width, int height) {int[] rgb = decodeYUV420SP(data, width / 8, height / 8);Bitmap bmp = Bitmap.createBitmap(rgb, width / 8, height / 8, Bitmap.Config.ARGB_8888);//这里报错if (bmp != null) {//取以中心点宽高10像素的图片来分析Bitmap resizeBitmap = Bitmap.createBitmap(bmp, bmp.getWidth() / 2, bmp.getHeight() / 2, 10, 10);float color = (float) getAverageColor(resizeBitmap);DecimalFormat decimalFormat1 = new DecimalFormat("0.00");String percent = decimalFormat1.format(color / -16777216);float floatPercent = Float.parseFloat(percent);isWeakLight = floatPercent >= 0.99 && floatPercent <= 1.00;
// Log.i(TAG,"isWeakLight "+isWeakLight);if (null != resizeBitmap) {resizeBitmap.recycle();}bmp.recycle();}}
}
DecodeHintManager.java
final class DecodeHintManager {private static final String TAG = DecodeHintManager.class.getSimpleName();// This pattern is used in decoding integer arrays.private static final Pattern COMMA = Pattern.compile(",");private DecodeHintManager() {}/*** <p>Split a query string into a list of name-value pairs.</p>* * <p>This is an alternative to the {@link Uri#getQueryParameterNames()} and* {@link Uri#getQueryParameters(String)}, which are quirky and not suitable* for exist-only Uri parameters.</p>* * <p>This method ignores multiple parameters with the same name and returns the* first one only. This is technically incorrect, but should be acceptable due* to the method of processing Hints: no multiple values for a hint.</p>* * @param query query to split* @return name-value pairs*/private static Map<String,String> splitQuery(String query) {Map<String,String> map = new HashMap<>();int pos = 0;while (pos < query.length()) {if (query.charAt(pos) == '&') {// Skip consecutive ampersand separators.pos ++;continue;}int amp = query.indexOf('&', pos);int equ = query.indexOf('=', pos);if (amp < 0) {// This is the last element in the query, no more ampersand elements.String name;String text;if (equ < 0) {// No equal signname = query.substring(pos);name = name.replace('+', ' '); // Preemptively decode +name = Uri.decode(name);text = "";} else {// Split name and text.name = query.substring(pos, equ);name = name.replace('+', ' '); // Preemptively decode +name = Uri.decode(name);text = query.substring(equ + 1);text = text.replace('+', ' '); // Preemptively decode +text = Uri.decode(text);}if (!map.containsKey(name)) {map.put(name, text);}break;}if (equ < 0 || equ > amp) {// No equal sign until the &: this is a simple parameter with no value.String name = query.substring(pos, amp);name = name.replace('+', ' '); // Preemptively decode +name = Uri.decode(name);if (!map.containsKey(name)) {map.put(name, "");}pos = amp + 1;continue;}String name = query.substring(pos, equ);name = name.replace('+', ' '); // Preemptively decode +name = Uri.decode(name);String text = query.substring(equ+1, amp);text = text.replace('+', ' '); // Preemptively decode +text = Uri.decode(text);if (!map.containsKey(name)) {map.put(name, text);}pos = amp + 1;}return map;}static Map<DecodeHintType,?> parseDecodeHints(Uri inputUri) {String query = inputUri.getEncodedQuery();if (query == null || query.isEmpty()) {return null;}// Extract parametersMap<String, String> parameters = splitQuery(query);Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);for (DecodeHintType hintType: DecodeHintType.values()) {if (hintType == DecodeHintType.CHARACTER_SET ||hintType == DecodeHintType.NEED_RESULT_POINT_CALLBACK ||hintType == DecodeHintType.POSSIBLE_FORMATS) {continue; // This hint is specified in another way}String parameterName = hintType.name();String parameterText = parameters.get(parameterName);if (parameterText == null) {continue;}if (hintType.getValueType().equals(Object.class)) {// This is an unspecified type of hint content. Use the value as is.// TODO: Can we make a different assumption on this?hints.put(hintType, parameterText);continue;}if (hintType.getValueType().equals(Void.class)) {// Void hints are just flags: use the constant specified by DecodeHintTypehints.put(hintType, Boolean.TRUE);continue;}if (hintType.getValueType().equals(String.class)) {// A string hint: use the decoded value.hints.put(hintType, parameterText);continue;}if (hintType.getValueType().equals(Boolean.class)) {// A boolean hint: a few values for false, everything else is true.// An empty parameter is simply a flag-style parameter, assuming trueif (parameterText.isEmpty()) {hints.put(hintType, Boolean.TRUE);} else if ("0".equals(parameterText) || "false".equalsIgnoreCase(parameterText) || "no".equalsIgnoreCase(parameterText)) {hints.put(hintType, Boolean.FALSE);} else {hints.put(hintType, Boolean.TRUE);}continue;}if (hintType.getValueType().equals(int[].class)) {// An integer array. Used to specify valid lengths.// Strip a trailing comma as in Java style array initialisers.if (!parameterText.isEmpty() && parameterText.charAt(parameterText.length() - 1) == ',') {parameterText = parameterText.substring(0, parameterText.length() - 1);}String[] values = COMMA.split(parameterText);int[] array = new int[values.length];for (int i = 0; i < values.length; i++) {try {array[i] = Integer.parseInt(values[i]);} catch (NumberFormatException ignored) {Log.w(TAG, "Skipping array of integers hint " + hintType + " due to invalid numeric value: '" + values[i] + '\'');array = null;break;}}if (array != null) {hints.put(hintType, array);}continue;} Log.w(TAG, "Unsupported hint type '" + hintType + "' of type " + hintType.getValueType());}Log.i(TAG, "Hints from the URI: " + hints);return hints;}static Map<DecodeHintType, Object> parseDecodeHints(Intent intent) {Bundle extras = intent.getExtras();if (extras == null || extras.isEmpty()) {return null;}Map<DecodeHintType,Object> hints = new EnumMap<>(DecodeHintType.class);for (DecodeHintType hintType: DecodeHintType.values()) {if (hintType == DecodeHintType.CHARACTER_SET ||hintType == DecodeHintType.NEED_RESULT_POINT_CALLBACK ||hintType == DecodeHintType.POSSIBLE_FORMATS) {continue; // This hint is specified in another way}String hintName = hintType.name();if (extras.containsKey(hintName)) {if (hintType.getValueType().equals(Void.class)) {// Void hints are just flags: use the constant specified by the DecodeHintTypehints.put(hintType, Boolean.TRUE);} else {Object hintData = extras.get(hintName);if (hintType.getValueType().isInstance(hintData)) {hints.put(hintType, hintData);} else {Log.w(TAG, "Ignoring hint " + hintType + " because it is not assignable from " + hintData);}}}}Log.i(TAG, "Hints from the Intent: " + hints);return hints;}}
DecodeThread.java
final class DecodeThread extends Thread {public static final String BARCODE_BITMAP = "barcode_bitmap";public static final String BARCODE_SCALED_FACTOR = "barcode_scaled_factor";private final CaptureActivity activity;private final Map<DecodeHintType, Object> hints;private Handler handler;private final CountDownLatch handlerInitLatch;public DecodeThread(CaptureActivity activity,Collection<BarcodeFormat> decodeFormats,String characterSet,ResultPointCallback resultPointCallback) {this.activity = activity;handlerInitLatch = new CountDownLatch(1);hints = new EnumMap<>(DecodeHintType.class);// The prefs can't change while the thread is running, so pick them up once here.if (decodeFormats == null || decodeFormats.isEmpty()) {
// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);decodeFormats = EnumSet.noneOf(BarcodeFormat.class);/*if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_1D_PRODUCT, true)) {decodeFormats.addAll(DecodeFormatManager.PRODUCT_FORMATS);}if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_1D_INDUSTRIAL, true)) {decodeFormats.addAll(DecodeFormatManager.INDUSTRIAL_FORMATS);}*/
// if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_QR, true)) {decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
// }/* if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_DATA_MATRIX, true)) {decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);}if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_AZTEC, false)) {decodeFormats.addAll(DecodeFormatManager.AZTEC_FORMATS);}if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_PDF417, false)) {decodeFormats.addAll(DecodeFormatManager.PDF417_FORMATS);}*/}hints.put(DecodeHintType.POSSIBLE_FORMATS, DecodeFormatManager.QR_CODE_FORMATS);if (characterSet != null) {hints.put(DecodeHintType.CHARACTER_SET, characterSet);}hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);Log.i("DecodeThread", "Hints: " + hints);}Handler getHandler() {try {handlerInitLatch.await();} catch (InterruptedException ie) {// continue?}return handler;}@Overridepublic void run() {Looper.prepare();handler = new DecodeHandler(activity, hints);handlerInitLatch.countDown();Looper.loop();}}
ImageUtil.java
public class ImageUtil {@TargetApi(19)public static String getImageAbsolutePath(Context context, Uri imageUri) {if (context == null || imageUri == null)return null;if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, imageUri)) {if (isExternalStorageDocument(imageUri)) {String docId = DocumentsContract.getDocumentId(imageUri);String[] split = docId.split(":");String type = split[0];if ("primary".equalsIgnoreCase(type)) {return Environment.getExternalStorageDirectory() + "/" + split[1];}} else if (isDownloadsDocument(imageUri)) {String id = DocumentsContract.getDocumentId(imageUri);Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));return getDataColumn(context, contentUri, null, null);} else if (isMediaDocument(imageUri)) {String docId = DocumentsContract.getDocumentId(imageUri);String[] split = docId.split(":");String type = split[0];Uri contentUri = null;if ("image".equals(type)) {contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;} else if ("video".equals(type)) {contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;} else if ("audio".equals(type)) {contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;}String selection = MediaStore.Images.Media._ID + "=?";String[] selectionArgs = new String[]{split[1]};return getDataColumn(context, contentUri, selection, selectionArgs);}} // MediaStore (and general)else if ("content".equalsIgnoreCase(imageUri.getScheme())) {// Return the remote addressif (isGooglePhotosUri(imageUri))return imageUri.getLastPathSegment();return getDataColumn(context, imageUri, null, null);}// Fileelse if ("file".equalsIgnoreCase(imageUri.getScheme())) {return imageUri.getPath();}return null;}public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {Cursor cursor = null;String column = MediaStore.Images.Media.DATA;String[] projection = {column};try {cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);if (cursor != null && cursor.moveToFirst()) {int index = cursor.getColumnIndexOrThrow(column);return cursor.getString(index);}} finally {if (cursor != null)cursor.close();}return null;}/*** @param uri The Uri to check.* @return Whether the Uri authority is ExternalStorageProvider.*/public static boolean isExternalStorageDocument(Uri uri) {return "com.android.externalstorage.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is DownloadsProvider.*/public static boolean isDownloadsDocument(Uri uri) {return "com.android.providers.downloads.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is MediaProvider.*/public static boolean isMediaDocument(Uri uri) {return "com.android.providers.media.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is Google Photos.*/public static boolean isGooglePhotosUri(Uri uri) {return "com.google.android.apps.photos.content".equals(uri.getAuthority());}
}
InactivityTimer.java
public final class InactivityTimer {private static final String TAG = InactivityTimer.class.getSimpleName();private static final long INACTIVITY_DELAY_MS = 5 * 60 * 1000L;private final Activity activity;private final BroadcastReceiver powerStatusReceiver;private boolean registered;private AsyncTask<Object, Object, Object> inactivityTask;public InactivityTimer(Activity activity) {this.activity = activity;powerStatusReceiver = new PowerStatusReceiver();registered = false;onActivity();}public synchronized void onActivity() {cancel();inactivityTask = new InactivityAsyncTask();inactivityTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);}public synchronized void onPause() {cancel();if (registered) {activity.unregisterReceiver(powerStatusReceiver);registered = false;} else {Log.w(TAG, "PowerStatusReceiver was never registered?");}}public synchronized void onResume() {if (registered) {Log.w(TAG, "PowerStatusReceiver was already registered?");} else {activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));registered = true;}onActivity();}private synchronized void cancel() {AsyncTask<?, ?, ?> task = inactivityTask;if (task != null) {task.cancel(true);inactivityTask = null;}}public void shutdown() {cancel();}private final class PowerStatusReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {// 0 indicates that we're on batteryboolean onBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0;if (onBatteryNow) {InactivityTimer.this.onActivity();} else {InactivityTimer.this.cancel();}}}}private final class InactivityAsyncTask extends AsyncTask<Object, Object, Object> {@Overrideprotected Object doInBackground(Object... objects) {try {Thread.sleep(INACTIVITY_DELAY_MS);Log.i(TAG, "Finishing activity due to inactivity");activity.finish();} catch (InterruptedException e) {// continue without killing}return null;}}}
Intents.java
public final class Intents {private Intents() {}public static final class Scan {/*** Send this intent to open the Barcodes app in scanning mode, find a barcode, and return* the results.*/public static final String ACTION = "com.google.zxing.client.android.SCAN";/*** By default, sending this will decode all barcodes that we understand. However it* may be useful to limit scanning to certain formats. Use* {@link android.content.Intent#putExtra(String, String)} with one of the values below.** Setting this is effectively shorthand for setting explicit formats with {@link #FORMATS}.* It is overridden by that setting.*/public static final String MODE = "SCAN_MODE";/*** Decode only UPC and EAN barcodes. This is the right choice for shopping apps which get* prices, reviews, etc. for products.*/public static final String PRODUCT_MODE = "PRODUCT_MODE";/*** Decode only 1D barcodes.*/public static final String ONE_D_MODE = "ONE_D_MODE";/*** Decode only QR codes.*/public static final String QR_CODE_MODE = "QR_CODE_MODE";/*** Decode only Data Matrix codes.*/public static final String DATA_MATRIX_MODE = "DATA_MATRIX_MODE";/*** Decode only Aztec.*/public static final String AZTEC_MODE = "AZTEC_MODE";/*** Decode only PDF417.*/public static final String PDF417_MODE = "PDF417_MODE";/*** Comma-separated list of formats to scan for. The values must match the names of* {@link com.google.zxing.BarcodeFormat}s, e.g. {@link com.google.zxing.BarcodeFormat#EAN_13}.* Example: "EAN_13,EAN_8,QR_CODE". This overrides {@link #MODE}.*/public static final String FORMATS = "SCAN_FORMATS";/*** Optional parameter to specify the id of the camera from which to recognize barcodes.* Overrides the default camera that would otherwise would have been selected.* If provided, should be an int.*/public static final String CAMERA_ID = "SCAN_CAMERA_ID";/*** @see com.google.zxing.DecodeHintType#CHARACTER_SET*/public static final String CHARACTER_SET = "CHARACTER_SET";/*** Optional parameters to specify the width and height of the scanning rectangle in pixels.* The app will try to honor these, but will clamp them to the size of the preview frame.* You should specify both or neither, and pass the size as an int.*/public static final String WIDTH = "SCAN_WIDTH";public static final String HEIGHT = "SCAN_HEIGHT";/*** Desired duration in milliseconds for which to pause after a successful scan before* returning to the calling intent. Specified as a long, not an integer!* For example: 1000L, not 1000.*/public static final String RESULT_DISPLAY_DURATION_MS = "RESULT_DISPLAY_DURATION_MS";/*** Prompt to show on-screen when scanning by intent. Specified as a {@link String}.*/public static final String PROMPT_MESSAGE = "PROMPT_MESSAGE";/*** If a barcode is found, Barcodes returns {@link android.app.Activity#RESULT_OK} to* {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)}* of the app which requested the scan via* {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}* The barcodes contents can be retrieved with* {@link android.content.Intent#getStringExtra(String)}.* If the user presses Back, the result code will be {@link android.app.Activity#RESULT_CANCELED}.*/public static final String RESULT = "SCAN_RESULT";/*** Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_FORMAT}* to determine which barcode format was found.* See {@link com.google.zxing.BarcodeFormat} for possible values.*/public static final String RESULT_FORMAT = "SCAN_RESULT_FORMAT";/*** Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_UPC_EAN_EXTENSION}* to return the content of any UPC extension barcode that was also found. Only applicable* to {@link com.google.zxing.BarcodeFormat#UPC_A} and {@link com.google.zxing.BarcodeFormat#EAN_13}* formats.*/public static final String RESULT_UPC_EAN_EXTENSION = "SCAN_RESULT_UPC_EAN_EXTENSION";/*** Call {@link android.content.Intent#getByteArrayExtra(String)} with {@link #RESULT_BYTES}* to get a {@code byte[]} of raw bytes in the barcode, if available.*/public static final String RESULT_BYTES = "SCAN_RESULT_BYTES";/*** Key for the value of {@link com.google.zxing.ResultMetadataType#ORIENTATION}, if available.* Call {@link android.content.Intent#getIntArrayExtra(String)} with {@link #RESULT_ORIENTATION}.*/public static final String RESULT_ORIENTATION = "SCAN_RESULT_ORIENTATION";/*** Key for the value of {@link com.google.zxing.ResultMetadataType#ERROR_CORRECTION_LEVEL}, if available.* Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_ERROR_CORRECTION_LEVEL}.*/public static final String RESULT_ERROR_CORRECTION_LEVEL = "SCAN_RESULT_ERROR_CORRECTION_LEVEL";/*** Prefix for keys that map to the values of {@link com.google.zxing.ResultMetadataType#BYTE_SEGMENTS},* if available. The actual values will be set under a series of keys formed by adding 0, 1, 2, ...* to this prefix. So the first byte segment is under key "SCAN_RESULT_BYTE_SEGMENTS_0" for example.* Call {@link android.content.Intent#getByteArrayExtra(String)} with these keys.*/public static final String RESULT_BYTE_SEGMENTS_PREFIX = "SCAN_RESULT_BYTE_SEGMENTS_";/*** Setting this to false will not save scanned codes in the history. Specified as a {@code boolean}.*/public static final String SAVE_HISTORY = "SAVE_HISTORY";private Scan() {}}public static final class History {public static final String ITEM_NUMBER = "ITEM_NUMBER";private History() {}}public static final class Encode {/*** Send this intent to encode a piece of data as a QR code and display it full screen, so* that another person can scan the barcode from your screen.*/public static final String ACTION = "com.google.zxing.client.android.ENCODE";/*** The data to encode. Use {@link android.content.Intent#putExtra(String, String)} or* {@link android.content.Intent#putExtra(String, android.os.Bundle)},* depending on the type and format specified. Non-QR Code formats should* just use a String here. For QR Code, see Contents for details.*/public static final String DATA = "ENCODE_DATA";/*** The type of data being supplied if the format is QR Code. Use* {@link android.content.Intent#putExtra(String, String)} with one of {@link Contents.Type}.*/public static final String TYPE = "ENCODE_TYPE";/*** The barcode format to be displayed. If this isn't specified or is blank,* it defaults to QR Code. Use {@link android.content.Intent#putExtra(String, String)}, where* format is one of {@link com.google.zxing.BarcodeFormat}.*/public static final String FORMAT = "ENCODE_FORMAT";/*** Normally the contents of the barcode are displayed to the user in a TextView. Setting this* boolean to false will hide that TextView, showing only the encode barcode.*/public static final String SHOW_CONTENTS = "ENCODE_SHOW_CONTENTS";private Encode() {}}public static final class SearchBookContents {/*** Use Google Book Search to search the contents of the book provided.*/public static final String ACTION = "com.google.zxing.client.android.SEARCH_BOOK_CONTENTS";/*** The book to search, identified by ISBN number.*/public static final String ISBN = "ISBN";/*** An optional field which is the text to search for.*/public static final String QUERY = "QUERY";private SearchBookContents() {}}public static final class WifiConnect {/*** Internal intent used to trigger connection to a wi-fi network.*/public static final String ACTION = "com.google.zxing.client.android.WIFI_CONNECT";/*** The network to connect to, all the configuration provided here.*/public static final String SSID = "SSID";/*** The network to connect to, all the configuration provided here.*/public static final String TYPE = "TYPE";/*** The network to connect to, all the configuration provided here.*/public static final String PASSWORD = "PASSWORD";private WifiConnect() {}}public static final class Share {/*** Give the user a choice of items to encode as a barcode, then render it as a QR Code and* display onscreen for a friend to scan with their phone.*/public static final String ACTION = "com.google.zxing.client.android.SHARE";private Share() {}}
}
ViewfinderResultPointCallback.java
final class ViewfinderResultPointCallback implements ResultPointCallback {private final ViewfinderView viewfinderView;ViewfinderResultPointCallback(ViewfinderView viewfinderView) {this.viewfinderView = viewfinderView;}@Overridepublic void foundPossibleResultPoint(ResultPoint point) {viewfinderView.addPossibleResultPoint(point);}}
CodeCreator.java
public class CodeCreator {/*logo*/private static Bitmap logoBitmap;/*生成二维码*/public static Bitmap createQRCode(String content, int w, int h,Bitmap logo) throws WriterException {if (TextUtils.isEmpty(content)) {return null;}/*偏移量*/int offsetX = w / 2;int offsetY = h / 2;/*生成logo*/if (logo!=null){Matrix matrix = new Matrix();float scaleFactor = Math.min(w * 1.0f / 5 / logo.getWidth(), h * 1.0f / 5 /logo.getHeight());matrix.postScale(scaleFactor,scaleFactor);logoBitmap= Bitmap.createBitmap(logo, 0, 0, logo.getWidth(), logo.getHeight(), matrix, true);}/*如果log不为null,重新计算偏移量*/int logoW = 0;int logoH = 0;if (logoBitmap != null) {logoW = logoBitmap.getWidth();logoH = logoBitmap.getHeight();offsetX = (w - logoW) / 2;offsetY = (h - logoH) / 2;}/*指定为UTF-8*/Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();hints.put(EncodeHintType.CHARACTER_SET, "utf-8");//容错级别hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);//设置空白边距的宽度hints.put(EncodeHintType.MARGIN, 0);// 生成二维矩阵,编码时指定大小,不要生成了图片以后再进行缩放,这样会模糊导致识别失败BitMatrix matrix = new MultiFormatWriter().encode(content,BarcodeFormat.QR_CODE, w, h, hints);// 二维矩阵转为一维像素数组,也就是一直横着排了int[] pixels = new int[w * h];for (int y = 0; y < h; y++) {for (int x = 0; x < w; x++) {if(x >= offsetX && x < offsetX + logoW && y>= offsetY && y < offsetY + logoH){int pixel = logoBitmap.getPixel(x-offsetX,y-offsetY);if(pixel == 0){if(matrix.get(x, y)){pixel = 0xff000000;}else{pixel = 0xffffffff;}}pixels[y * w + x] = pixel;}else{if (matrix.get(x, y)) {pixels[y * w + x] = 0xff000000;} else {pixels[y * w + x] = 0xffffffff;}}}}Bitmap bitmap = Bitmap.createBitmap(w, h,Bitmap.Config.ARGB_8888);bitmap.setPixels(pixels, 0, w, 0, 0, w, h);return bitmap;}}
QRCodeEncoder.java
public final class QRCodeEncoder {private static final String TAG = QRCodeEncoder.class.getSimpleName();private static final int WHITE = 0xFFFFFFFF;private static final int BLACK = 0xFF000000;public static Bitmap encodeAsBitmap(String contents, int dimension) throws WriterException {String contentsToEncode = contents;if (contentsToEncode == null) {return null;}Map<EncodeHintType, Object> hints = null;String encoding = guessAppropriateEncoding(contentsToEncode);if (encoding != null) {hints = new EnumMap<>(EncodeHintType.class);hints.put(EncodeHintType.CHARACTER_SET, encoding);}BitMatrix result;try {result = new MultiFormatWriter().encode(contentsToEncode, BarcodeFormat.QR_CODE, dimension, dimension, hints);} catch (IllegalArgumentException iae) {// Unsupported formatreturn null;}int width = result.getWidth();int height = result.getHeight();int[] pixels = new int[width * height];for (int y = 0; y < height; y++) {int offset = y * width;for (int x = 0; x < width; x++) {pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;}}Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);bitmap.setPixels(pixels, 0, width, 0, 0, width, height);return bitmap;}private static String guessAppropriateEncoding(CharSequence contents) {// Very crude at the momentfor (int i = 0; i < contents.length(); i++) {if (contents.charAt(i) > 0xFF) {return "UTF-8";}}return null;}}
ViewfinderResultPointCallback.java
public final class ViewfinderResultPointCallback implements ResultPointCallback {private final ViewfinderView viewfinderView;public ViewfinderResultPointCallback(ViewfinderView viewfinderView) {this.viewfinderView = viewfinderView;}@Overridepublic void foundPossibleResultPoint(ResultPoint point) {viewfinderView.addPossibleResultPoint(point);}}
ViewfinderView.java
public final class ViewfinderView extends View{private static final long ANIMATION_DELAY = 10;private static final int CORNER_RECT_HEIGHT = 40;private static final int CORNER_RECT_WIDTH = 8;private static final int OPAQUE = 255;private static final int[] SCANNER_ALPHA = new int[]{0, 64, 128, 192, OPAQUE, 192, 128, 64};private static final int SCANNER_LINE_HEIGHT = 10;private static final int SCANNER_LINE_MOVE_DISTANCE = 5;public static int scannerEnd = 0;public static int scannerStart = 0;private CameraManager cameraManager;private final int cornerColor;private final int frameColor;private String labelText;private int labelTextColor;private float labelTextSize;private final int laserColor;private Collection<ResultPoint> lastPossibleResultPoints;private final IMResUtil mImResUtil;private final int maskColor;private final Paint paint = new Paint();private Collection<ResultPoint> possibleResultPoints;private Bitmap resultBitmap;private final int resultColor;private final int resultPointColor;private int scannerAlpha;public ViewfinderView(Context context, AttributeSet attrs) {super(context, attrs);this.mImResUtil = new IMResUtil(context);TypedArray array = context.obtainStyledAttributes(attrs, this.mImResUtil.getStyleableArray("ViewfinderView"));this.laserColor = array.getColor(this.mImResUtil.getStyleable("ViewfinderView_device_qrcode_laser_color"), 65280);this.cornerColor = array.getColor(this.mImResUtil.getStyleable("ViewfinderView_device_qrcode_corner_color"), 65280);this.frameColor = array.getColor(this.mImResUtil.getStyleable("ViewfinderView_device_qrcode_frame_color"), 16777215);this.resultPointColor = array.getColor(this.mImResUtil.getStyleable("ViewfinderView_device_qrcode_result_point_color"), -1056964864);this.maskColor = array.getColor(this.mImResUtil.getStyleable("ViewfinderView_device_qrcode_mask_color"), 1610612736);this.resultColor = array.getColor(this.mImResUtil.getStyleable("ViewfinderView_device_qrcode_result_color"), -1342177280);this.labelTextColor = array.getColor(this.mImResUtil.getStyleable("ViewfinderView_device_qrcode_label_text_color"), -1862270977);this.labelText = array.getString(this.mImResUtil.getStyleable("ViewfinderView_device_qrcode_label_text"));this.labelTextSize = (float) array.getDimensionPixelSize(this.mImResUtil.getStyleable("ViewfinderView_device_qrcode_label_text_size"), (int) TypedValue.applyDimension(2, 16.0f, getResources().getDisplayMetrics()));this.paint.setAntiAlias(true);this.scannerAlpha = 0;this.possibleResultPoints = new HashSet(5);}public void setCameraManager(CameraManager cameraManager) {this.cameraManager = cameraManager;}public void onDraw(Canvas canvas) {Rect frame = this.cameraManager.getFramingRect();if (frame != null) {if (scannerStart == 0 || scannerEnd == 0) {scannerStart = frame.top;scannerEnd = frame.bottom;}drawExterior(canvas, frame, canvas.getWidth(), canvas.getHeight());if (this.resultBitmap != null) {this.paint.setAlpha(OPAQUE);canvas.drawBitmap(this.resultBitmap, (float) frame.left, (float) frame.top, this.paint);return;}drawFrame(canvas, frame);drawCorner(canvas, frame);drawLaserScanner(canvas, frame);drawTextInfo(canvas, frame);Collection<ResultPoint> currentPossible = this.possibleResultPoints;Collection<ResultPoint> currentLast = this.lastPossibleResultPoints;if (currentPossible.isEmpty()) {this.lastPossibleResultPoints = null;} else {this.possibleResultPoints = new HashSet(5);this.lastPossibleResultPoints = currentPossible;this.paint.setAlpha(OPAQUE);this.paint.setColor(this.resultPointColor);for (ResultPoint point : currentPossible) {canvas.drawCircle(((float) frame.left) + point.getX(), ((float) frame.top) + point.getY(), 6.0f, this.paint);}}if (currentLast != null) {this.paint.setAlpha(127);this.paint.setColor(this.resultPointColor);for (ResultPoint point2 : currentLast) {canvas.drawCircle(((float) frame.left) + point2.getX(), ((float) frame.top) + point2.getY(), 3.0f, this.paint);}}postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom);}}private void drawTextInfo(Canvas canvas, Rect frame) {this.paint.setColor(this.labelTextColor);this.paint.setTextSize(TypedValue.applyDimension(2, this.labelTextSize, getResources().getDisplayMetrics()));this.paint.setTextAlign(Align.CENTER);}private void drawCorner(Canvas canvas, Rect frame) {this.paint.setColor(this.cornerColor);canvas.drawRect((float) frame.left, (float) frame.top, (float) (frame.left + CORNER_RECT_WIDTH), (float) (frame.top + CORNER_RECT_HEIGHT), this.paint);canvas.drawRect((float) frame.left, (float) frame.top, (float) (frame.left + CORNER_RECT_HEIGHT), (float) (frame.top + CORNER_RECT_WIDTH), this.paint);canvas.drawRect((float) (frame.right - 8), (float) frame.top, (float) frame.right, (float) (frame.top + CORNER_RECT_HEIGHT), this.paint);canvas.drawRect((float) (frame.right - 40), (float) frame.top, (float) frame.right, (float) (frame.top + CORNER_RECT_WIDTH), this.paint);canvas.drawRect((float) frame.left, (float) (frame.bottom - 8), (float) (frame.left + CORNER_RECT_HEIGHT), (float) frame.bottom, this.paint);canvas.drawRect((float) frame.left, (float) (frame.bottom - 40), (float) (frame.left + CORNER_RECT_WIDTH), (float) frame.bottom, this.paint);canvas.drawRect((float) (frame.right - 8), (float) (frame.bottom - 40), (float) frame.right, (float) frame.bottom, this.paint);canvas.drawRect((float) (frame.right - 40), (float) (frame.bottom - 8), (float) frame.right, (float) frame.bottom, this.paint);}private void drawLaserScanner(Canvas canvas, Rect frame) {this.paint.setColor(this.laserColor);LinearGradient linearGradient = new LinearGradient((float) frame.left, (float) scannerStart, (float) frame.left, (float) (scannerStart + 10), shadeColor(this.laserColor), this.laserColor, TileMode.MIRROR);RadialGradient radialGradient = new RadialGradient((float) (frame.left + (frame.width() / 2)), (float) (scannerStart + 5), 360.0f, this.laserColor, shadeColor(this.laserColor), TileMode.MIRROR);SweepGradient sweepGradient = new SweepGradient((float) (frame.left + (frame.width() / 2)), (float) (scannerStart + 10), shadeColor(this.laserColor), this.laserColor);ComposeShader composeShader = new ComposeShader(radialGradient, linearGradient, Mode.ADD);this.paint.setShader(radialGradient);if (scannerStart <= scannerEnd) {canvas.drawOval(new RectF((float) (frame.left + 20), (float) scannerStart, (float) (frame.right - 20), (float) (scannerStart + 10)), this.paint);scannerStart += 5;} else {scannerStart = frame.top;}this.paint.setShader(null);}public int shadeColor(int color) {return Integer.valueOf("20" + Integer.toHexString(color).substring(2), 16).intValue();}private void drawFrame(Canvas canvas, Rect frame) {this.paint.setColor(this.frameColor);canvas.drawRect((float) frame.left, (float) frame.top, (float) (frame.right + 1), (float) (frame.top + 2), this.paint);canvas.drawRect((float) frame.left, (float) (frame.top + 2), (float) (frame.left + 2), (float) (frame.bottom - 1), this.paint);canvas.drawRect((float) (frame.right - 1), (float) frame.top, (float) (frame.right + 1), (float) (frame.bottom - 1), this.paint);canvas.drawRect((float) frame.left, (float) (frame.bottom - 1), (float) (frame.right + 1), (float) (frame.bottom + 1), this.paint);}private void drawExterior(Canvas canvas, Rect frame, int width, int height) {this.paint.setColor(this.resultBitmap != null ? this.resultColor : this.maskColor);canvas.drawRect(0.0f, 0.0f, (float) width, (float) frame.top, this.paint);canvas.drawRect(0.0f, (float) frame.top, (float) frame.left, (float) (frame.bottom + 1), this.paint);canvas.drawRect((float) (frame.right + 1), (float) frame.top, (float) width, (float) (frame.bottom + 1), this.paint);canvas.drawRect(0.0f, (float) (frame.bottom + 1), (float) width, (float) height, this.paint);}public void drawViewfinder() {this.resultBitmap = null;invalidate();}public void drawResultBitmap(Bitmap barcode) {this.resultBitmap = barcode;invalidate();}public void addPossibleResultPoint(ResultPoint point) {this.possibleResultPoints.add(point);}public void setLabelText(String labelText) {this.labelText = labelText;}public void setLabelTextColor(int labelTextColor) {this.labelTextColor = labelTextColor;}public void setLabelTextSize(float labelTextSize) {this.labelTextSize = labelTextSize;}
}
FileUtil.java
public class FileUtil {private static String TAG = "FileUtil";public static String randomFileName(String ext) {return Long.toString(System.currentTimeMillis()) + ext;}public static boolean deleteFile(String name) {File file = new File(name);if (file.exists()) {return file.delete();}return false;}@SuppressWarnings("deprecation")public static String rotateAndSaveBitmap(File file, int outW, int outH, String outPath, Bitmap.CompressFormat format, int quality) {int originW;int originH;int orientation = ExifInterface.ORIENTATION_NORMAL;try {ExifInterface exif = new ExifInterface(file.getAbsolutePath());orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);int w = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);int h = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);switch (orientation) {case ExifInterface.ORIENTATION_UNDEFINED:return null;case ExifInterface.ORIENTATION_ROTATE_90:case ExifInterface.ORIENTATION_ROTATE_270:originW = h;originH = w;break;default:originW = w;originH = h;break;}} catch (Exception e) {return null;}// boost decode bitmap performanceint sampleSize;if (outW <= 0 && outH <= 0) {sampleSize = 1;outW = originW;outH = originH;} else if (outW <= 0 || outH <= 0) {if (outW <= 0) {sampleSize = originH / outH;outW = (int) (originW * (outH / (float) originH));} else {sampleSize = originW / outW;outH = (int) (originH * (outW / (float) originW));}} else {sampleSize = Math.min(originW / outW, originH / outH);}BitmapFactory.Options bmOptions = new BitmapFactory.Options();bmOptions.inJustDecodeBounds = false;bmOptions.inSampleSize = sampleSize;bmOptions.inPurgeable = true;Bitmap bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), bmOptions);float scaleX = outW;float scaleY = outH;Matrix matrix = new Matrix();switch (orientation) {case ExifInterface.ORIENTATION_NORMAL:scaleX /= bmp.getWidth();scaleY /= bmp.getHeight();matrix.postScale(scaleX, scaleY);break;case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:scaleX /= bmp.getWidth();scaleY /= bmp.getHeight();matrix.setScale(-1, 1);matrix.postScale(scaleX, scaleY);break;case ExifInterface.ORIENTATION_ROTATE_180:scaleX /= bmp.getWidth();scaleY /= bmp.getHeight();matrix.setRotate(180);matrix.postScale(scaleX, scaleY);break;case ExifInterface.ORIENTATION_FLIP_VERTICAL:scaleX /= bmp.getWidth();scaleY /= bmp.getHeight();matrix.setRotate(180);matrix.postScale(-1, 1);matrix.postScale(scaleX, scaleY);break;case ExifInterface.ORIENTATION_TRANSPOSE:scaleX /= bmp.getWidth();scaleY /= bmp.getHeight();matrix.setRotate(90);matrix.postScale(-1, 1);matrix.postScale(scaleX, scaleY);break;case ExifInterface.ORIENTATION_ROTATE_90:scaleX /= bmp.getHeight();scaleY /= bmp.getWidth();matrix.setRotate(90);matrix.postScale(scaleX, scaleY);break;case ExifInterface.ORIENTATION_TRANSVERSE:scaleX /= bmp.getWidth();scaleY /= bmp.getHeight();matrix.setRotate(-90);matrix.postScale(-1, 1);matrix.postScale(scaleX, scaleY);break;case ExifInterface.ORIENTATION_ROTATE_270:scaleX /= bmp.getHeight();scaleY /= bmp.getWidth();matrix.setRotate(-90);matrix.postScale(scaleX, scaleY);break;default:break;}Bitmap bmpNew = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);String newFile = bmp2File(bmpNew, outPath, format, quality);bmpNew.recycle();return newFile;}private static Point getBitmapSize(String file) {BitmapFactory.Options bmOptions = new BitmapFactory.Options();bmOptions.inJustDecodeBounds = true;BitmapFactory.decodeFile(file, bmOptions);int originW = bmOptions.outWidth;int originH = bmOptions.outHeight;return new Point(originW, originH);}@SuppressWarnings("deprecation")public static String saveBitmap(File f, int outW, int outH, String outPath, Bitmap.CompressFormat format, int quality) {String rotateFile = rotateAndSaveBitmap(f, outW, outH, outPath, format, quality);if (null != rotateFile)return rotateFile;if (f == null) return "";String file = f.getAbsolutePath();// Get the dimensions of the bitmapPoint size = getBitmapSize(file);int originW = size.x;int originH = size.y;// Determine how much to scale down the imageint scaleFactor;if (outW <= 0 && outH <= 0) {scaleFactor = 1;outW = originW;outH = originH;} else if (outW <= 0 || outH <= 0) {if (outW <= 0) {scaleFactor = originH / outH;outW = (int) (originW * (outH / (float) originH));} else {scaleFactor = originW / outW;outH = (int) (originH * (outW / (float) originW));}} else {scaleFactor = Math.min(originW / outW, originH / outH);}// Decode the image file into a Bitmap sized to fit out sizeBitmapFactory.Options bmOptions = new BitmapFactory.Options();bmOptions.inJustDecodeBounds = false;bmOptions.inSampleSize = scaleFactor;bmOptions.inPurgeable = true;Bitmap bmp = BitmapFactory.decodeFile(file, bmOptions);String newFile;if (bmp == null) {return "";}if (bmp.getWidth() != outW && bmp.getHeight() != outH) {Bitmap bmpNew = Bitmap.createScaledBitmap(bmp, outW, outH, true);newFile = bmp2File(bmpNew, outPath, format, quality);bmpNew.recycle();} else {newFile = bmp2File(bmp, outPath, format, quality);}bmp.recycle();return newFile;}public static Point calcScaleSize(String file, int outW, int outH) {int originW = 0;int originH = 0;try {ExifInterface exif = new ExifInterface(file);int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);int w = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);int h = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);switch (orientation) {case ExifInterface.ORIENTATION_UNDEFINED:break;case ExifInterface.ORIENTATION_ROTATE_90:case ExifInterface.ORIENTATION_ROTATE_270:originW = h;originH = w;break;default:originW = w;originH = h;break;}} catch (Exception e) {}if (0 == originW || 0 == originH) {Point size = getBitmapSize(file);originW = size.x;originH = size.y;}if (outW <= 0 && outH <= 0) {outW = originW;outH = originH;} else if (outW <= 0 || outH <= 0) {if (outW <= 0) {outW = (int) (originW * (outH / (float) originH));} else {outH = (int) (originH * (outW / (float) originW));}} else {}return new Point(outW, outH);}public static String bmp2File(Bitmap bmp, String filePath, Bitmap.CompressFormat format, int quality) {String fileName = FileUtil.randomFileName(Bitmap.CompressFormat.PNG == format ? ".png" : ".jpg");String fullPath = filePath + fileName;deleteFile(fileName);File file = new File(fullPath);try {file.createNewFile();} catch (IOException e) {e.printStackTrace();return null;}FileOutputStream fOut = null;try {fOut = new FileOutputStream(file);} catch (FileNotFoundException e) {e.printStackTrace();return null;}bmp.compress(format, quality, fOut);try {fOut.flush();fOut.close();fOut = null;} catch (IOException e) {e.printStackTrace();return null;} finally {try {if (null != fOut) {fOut.close();fOut = null;}} catch (Exception e) {e.printStackTrace();return null;}}return fileName;}public static final String insertImage(ContentResolver cr, String imagePath, String name, String description) throws FileNotFoundException {FileInputStream inStream = new FileInputStream(imagePath);return insertImage(cr, inStream, name, description);}/*** A copy of the Android internals insertImage method, this method populates* the meta data with DATE_ADDED and DATE_TAKEN. This fixes a common problem* where media that is inserted manually gets saved at the end of the* gallery (because date is not populated).** @see Images.Media#insertImage(ContentResolver,* Bitmap, String, String)*/public static final String insertImage(ContentResolver cr, FileInputStream inStream, String title, String description) {ContentValues values = new ContentValues();values.put(Images.Media.TITLE, title);values.put(Images.Media.DISPLAY_NAME, title);values.put(Images.Media.DESCRIPTION, description);values.put(Images.Media.MIME_TYPE, "image/jpeg");// Add the date meta data to ensure the image is added at the front of// the galleryvalues.put(Images.Media.DATE_ADDED, System.currentTimeMillis());values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis());Uri url = null;String stringUrl = null; /* value to be returned */try {url = cr.insert(Images.Media.EXTERNAL_CONTENT_URI, values);if (inStream != null) {OutputStream imageOut = cr.openOutputStream(url);byte[] buffer = new byte[1024];int count = 0;while ((count = inStream.read(buffer)) >= 0) {imageOut.write(buffer, 0, count);}imageOut.flush();inStream.close();long id = ContentUris.parseId(url);// Wait until MINI_KIND thumbnail is generated.Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MINI_KIND, null);// This is for backward compatibility.storeThumbnail(cr, miniThumb, id, 50F, 50F, Images.Thumbnails.MICRO_KIND);} else {cr.delete(url, null, null);url = null;}} catch (Exception e) {e.printStackTrace();if (url != null) {cr.delete(url, null, null);url = null;}}if (url != null) {stringUrl = url.toString();}return stringUrl;}/*** A copy of the Android internals StoreThumbnail method, it used with the* insertImage to populate the* android.provider.MediaStore.Images.Media#insertImage with all the correct* meta data. The StoreThumbnail method is private so it must be duplicated* here.** @see Images.Media (StoreThumbnail private* method)*/private static final Bitmap storeThumbnail(ContentResolver cr, Bitmap source, long id, float width, float height, int kind) {// create the matrix to scale itMatrix matrix = new Matrix();float scaleX = width / source.getWidth();float scaleY = height / source.getHeight();matrix.setScale(scaleX, scaleY);Bitmap thumb = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);ContentValues values = new ContentValues(4);values.put(Images.Thumbnails.KIND, kind);values.put(Images.Thumbnails.IMAGE_ID, (int) id);values.put(Images.Thumbnails.HEIGHT, thumb.getHeight());values.put(Images.Thumbnails.WIDTH, thumb.getWidth());Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);try {OutputStream thumbOut = cr.openOutputStream(url);thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);thumbOut.close();return thumb;} catch (FileNotFoundException ex) {return null;} catch (IOException ex) {return null;}}// /@SuppressLint("NewApi")public static String checkPicturePath(final Context context, final Uri uri) {final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;// DocumentProviderif (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {// ExternalStorageProviderif (isExternalStorageDocument(uri)) {final String docId = DocumentsContract.getDocumentId(uri);final String[] split = docId.split(":");final String type = split[0];if ("primary".equalsIgnoreCase(type)) {return Environment.getExternalStorageDirectory() + "/" + split[1];}}// DownloadsProviderelse if (isDownloadsDocument(uri)) {final String id = DocumentsContract.getDocumentId(uri);final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));return getDataColumn(context, contentUri, null, null);}// MediaProviderelse if (isMediaDocument(uri)) {final String docId = DocumentsContract.getDocumentId(uri);final String[] split = docId.split(":");final String type = split[0];Uri contentUri = null;if ("image".equals(type)) {contentUri = Images.Media.EXTERNAL_CONTENT_URI;} else if ("video".equals(type)) {contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;} else if ("audio".equals(type)) {contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;}final String selection = "_id=?";final String[] selectionArgs = new String[]{split[1]};return getDataColumn(context, contentUri, selection, selectionArgs);}}// MediaStore (and general)else if ("content".equalsIgnoreCase(uri.getScheme())) {// Return the remote addressif (isGooglePhotosUri(uri))return uri.getLastPathSegment();return getDataColumn(context, uri, null, null);}// Fileelse if ("file".equalsIgnoreCase(uri.getScheme())) {return uri.getPath();}return null;}/*** Get the value of the data column for this Uri. This is useful for* MediaStore Uris, and other file-based ContentProviders.** @param context The context.* @param uri The Uri to query.* @param selection (Optional) Filter used in the query.* @param selectionArgs (Optional) Selection arguments used in the query.* @return The value of the _data column, which is typically a file path.*/private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {Cursor cursor = null;final String column = "_data";final String[] projection = {column};try {cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);if (cursor != null && cursor.moveToFirst()) {final int index = cursor.getColumnIndexOrThrow(column);return cursor.getString(index);}} catch (Exception e) {e.printStackTrace();} finally {if (cursor != null)cursor.close();}return null;}/*** @param uri The Uri to check.* @return Whether the Uri authority is ExternalStorageProvider.*/private static boolean isExternalStorageDocument(Uri uri) {return "com.android.externalstorage.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is DownloadsProvider.*/private static boolean isDownloadsDocument(Uri uri) {return "com.android.providers.downloads.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is MediaProvider.*/private static boolean isMediaDocument(Uri uri) {return "com.android.providers.media.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is Google Photos.*/private static boolean isGooglePhotosUri(Uri uri) {return "com.google.android.apps.photos.content".equals(uri.getAuthority());}
}
IMResUtil.java
public class IMResUtil {private static final String TAG = "IMResUtil";private static IMResUtil instance;private Context context;private static Class id = null;private static Class layout = null;private static Class style = null;private static Class attr = null;private static Class styleable = null;public IMResUtil(Context paramContext) {this.context = paramContext.getApplicationContext();try {layout = Class.forName(this.context.getPackageName() + ".R$layout");} catch (ClassNotFoundException localClassNotFoundException2) {Log.e(TAG,"localClassNotFoundException2 = " + localClassNotFoundException2.toString());}try {id = Class.forName(this.context.getPackageName() + ".R$id");} catch (ClassNotFoundException localClassNotFoundException3) {Log.e(TAG,"localClassNotFoundException3 = " + localClassNotFoundException3.toString());}try {style = Class.forName(this.context.getPackageName() + ".R$style");} catch (ClassNotFoundException localClassNotFoundException5) {Log.e(TAG,"localClassNotFoundException5 = " + localClassNotFoundException5.toString());}try {attr = Class.forName(this.context.getPackageName() + ".R$attr");} catch (ClassNotFoundException localClassNotFoundException10) {Log.e(TAG,"localClassNotFoundException10 = " + localClassNotFoundException10.toString());}try {styleable = Class.forName(this.context.getPackageName() + ".R$styleable");} catch (ClassNotFoundException localClassNotFoundException10) {Log.e(TAG,"localClassNotFoundException10 = " + localClassNotFoundException10.toString());}}public static IMResUtil getResofR(Context paramContext) {if (instance == null)instance = new IMResUtil(paramContext);return instance;}public int getId(String paramString) {return getResofR(id, paramString);}public int getLayout(String paramString) {return getResofR(layout, paramString);}public int getStyle(String paramString) {return getResofR(style, paramString);}public int getAttr(String paramString) {return getResofR(attr, paramString);}public int getStyleable(String paramString) {return getResofR(styleable, paramString);}public int [] getStyleableArray(String paramString){try {Class clz = Class.forName(context.getPackageName()+".R$styleable");Field field = clz.getField(paramString);Object object = field.get(clz);int[] attr = (int[]) object;for (int i = 0; i < attr.length; i++) {Log.d(TAG, attr[i]+"");}return attr;} catch (Exception e) {Log.d(TAG, e.getMessage());e.printStackTrace();}return null;}private int getResofR(Class<?> paramClass, String paramString) {if (paramClass == null) {Log.d(TAG,"getRes(null," + paramString + ")");Log.d(TAG,"Class is null ,ResClass is not initialized.");return -1;
// throw new IllegalArgumentException("ResClass is not initialized.");}try {Field localField = paramClass.getField(paramString);int k = localField.getInt(paramString);return k;} catch (Exception localException) {Log.e(TAG,"getRes(" + paramClass.getName() + ", " + paramString + ")");Log.e(TAG,"Error getting resource. Make sure you have copied all resources (res/) from SDK to your project. ");Log.e(TAG,localException.getMessage());}return -1;}
}
xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"><!--抬头部分--><RelativeLayoutandroid:id="@+id/rl"android:layout_width="fill_parent"android:layout_height="50dp"android:background="#000000"android:paddingLeft="6dp"android:paddingStart="6dp"><ImageViewandroid:id="@+id/iv_qr_back"android:layout_width="30dp"android:layout_height="24dp"android:layout_centerVertical="true"android:src="@drawable/arrow_back"/><TextViewandroid:id="@+id/tv_qr_title"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_centerHorizontal="true"android:clickable="true"android:focusable="true"android:gravity="center"android:textColor="#ffffff"android:textSize="20sp"/><TextViewandroid:id="@+id/tv_qr_open_image"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_alignParentEnd="true"android:layout_alignParentRight="true"android:layout_marginEnd="15dp"android:layout_marginRight="15dp"android:clickable="true"android:focusable="true"android:gravity="center"android:textColor="#ffffff"android:textSize="20sp"/></RelativeLayout><!--主体部分--><SurfaceViewandroid:id="@+id/device_qrcode_preview_view"android:layout_width="fill_parent"android:layout_height="fill_parent"android:layout_below="@+id/rl"/><com.yzq.zxinglibrary.view.ViewfinderViewandroid:id="@+id/vv_qr_viewfinderView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"app:device_qrcode_corner_color="#005699"app:device_qrcode_frame_color="#90FFFFFF"app:device_qrcode_laser_color="#005699"app:device_qrcode_mask_color="#60000000"app:device_qrcode_result_color="#B0000000"app:device_qrcode_result_point_color="#C0FFFF00"/><!--app:label_text="将二维码放入框中,即可扫描"app:label_text_color="#ffffff"app:label_text_size="16sp"--><!--扫描灯光--><LinearLayoutandroid:id="@+id/bottomLayout"android:layout_width="match_parent"android:layout_height="96dp"android:layout_alignParentBottom="true"android:layout_gravity="bottom"android:background="#99000000"android:orientation="horizontal"><LinearLayoutandroid:id="@+id/flashLightLayout"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="center"android:orientation="vertical"><!--android:background="@drawable/device_qrcode_scan_flash_off"--><ImageViewandroid:id="@+id/flashLightIv"android:layout_width="36dp"android:layout_height="36dp"android:scaleType="centerCrop"android:tint="#ffffffff"/><TextViewandroid:id="@+id/flashLightTv"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:gravity="center"android:text="打开闪光灯"android:textColor="@color/result_text"/></LinearLayout><LinearLayoutandroid:id="@+id/albumLayout"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="center"android:orientation="vertical"><ImageViewandroid:id="@+id/img_phone"android:layout_width="36dp"android:layout_height="36dp"android:scaleType="centerCrop"android:tint="#ffffffff"android:src="@drawable/ic_photo"/><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:gravity="center"android:text="相册"android:textColor="@color/result_text"/></LinearLayout></LinearLayout></RelativeLayout>
清单文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.xxx.zxinglibrary"><uses-permission android:name="android.permission.CAMERA" /><uses-permission android:name="android.permission.FLASHLIGHT" /><uses-feature android:name="android.hardware.camera" /><uses-feature android:name="android.hardware.camera.autofocus" /><uses-permission android:name="android.permission.VIBRATE" /><uses-permission android:name="android.permission.WAKE_LOCK" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><application><activityandroid:name=".decode.CaptureActivity"android:configChanges="orientation|screenSize"android:screenOrientation="portrait"android:theme="@android:style/Theme.NoTitleBar"android:windowSoftInputMode="adjustPan|stateHidden" /></application></manifest>
以上的总结如果能够帮助到你,希望给个赞,如果不能,或者有更好的推荐,不管是拍砖,还是怎样,烦请留言,提供链接,改动方案,或者提供更好的优化建议,谢谢!我们定会虚心采纳,谢谢!
Android 基于Zxing的二维码扫描优化相关推荐
- 修正Android基于ZXing的二维码扫描——横竖屏自由切换
概述: 此博客是基于开源的框架ZXing.ZXing用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的端口.ZXing可以实现使用手机的内置的摄像头完成条形码的扫描及解码.该 ...
- 基于zxing的二维码扫描
前言 现在的应用中二维码扫描已经成为一个应用必不可少的功能,现在大部分Android二维码扫描都是基于zxing和Zbar,这文章就来介绍一下基于zxing的二维码扫描.先看下效果图 虽然现在的关于二 ...
- 基于zxing的二维码扫描(软件界面)
原文地址:https://www.jianshu.com/p/ee2ee7677831 前言 现在的应用中二维码扫描已经成为一个应用必不可少的功能,现在大部分Android二维码扫描都是基于zxing ...
- Android实战——Zxing实现二维码扫描
Zxing实现二维码扫描 前言: 本篇文章从初学者的角度出发,从一个不知道对二维码扫描怎么下手的工作者,需要一个简单的扫描功能的话,可以阅读该篇文章.作为Google开源框架Zxing,里面的文件很大 ...
- (转载)Android项目实战(二十八):使用Zxing实现二维码及优化实例
Android项目实战(二十八):使用Zxing实现二维码及优化实例 作者:听着music睡 字体:[增加 减小] 类型:转载 时间:2016-11-21 我要评论 这篇文章主要介绍了Android项 ...
- JAVA实现基于ZXing的二维码自动生成与图片合成
JAVA实现基于ZXing的二维码自动生成与图片合成 近日做项目需要生成带有信息的二维码,并嵌入到一张图片中.实现思路采用Zxing生成二维码,java图形库进行图片的嵌入. 生成二维码 ZXing是 ...
- 基于zxing生成二维码
目录 1.二维码的原理 2.基于zxing生成二维码 3.逻辑及其应用 完整代码:https://github.com/122537067/zxingQRcodeLogoColor 效果图: 1.二维 ...
- 二维码扫描优化及爱奇艺App的实践
写在前面 二维码是一个近几年来移动设备上超流行的一种编码方式(又称二维条码,QR Code,QR全称QuickResponse),它比传统的条形码(Bar Code)能存更多的信息,也能表示更多的数据 ...
- Android:实际运用Zxing集成二维码扫描 及 自定义扫码界面(demo源码)
二维码扫描,各大主流App必不可少的功能,而且google已将轮子替我们造好,直接拿来使用即可.以下是教学如何将Zxing开源库集成到自己项目中,并且自定义扫码界面,后期可根据自己的业务需求进行修改, ...
最新文章
- Codeforces Round #353 (Div. 2)
- C# 实现将 PDF 转文本的功能
- Silverlight+WCF 新手实例 象棋 WCF通讯跨域(十五)
- pagerank数据集_从数据结构到算法:图网络方法初探
- Animate.css介绍
- 数据库(三),底层算法
- 程序员亲身体验的学历之痛
- sqlsever 查询最新时间
- ACDSee Photo Studio 7 Mac(数字图像处理软件)
- [渝粤教育] 天津师范大学 教育心理学(唐卫海) 参考 资料
- Java利用递归算法实现24点游戏
- 使用frp进行内网穿透
- 线上线下协同发展,020 商业解析
- iOS 支付宝支付 微信支付SDK接口不统一? 盘他!
- html的css怎么设置深度,vue css 深度选择器
- poj 1729 Jack and Jill 1376 Robot 1324 Holedox Moving 1475 Pushing Boxes bfs + a*
- es6 取数组的第一个和最后一个_es6常用数组操作及技巧汇总
- Windows向Linux自动传输文件,Windows向虚拟机Linux传输文件方法
- android控制电路板的开关灯,一文解读LED触摸调光台灯控制电路板的工作原理
- 仓库 store getter
热门文章
- 【区块链 | ENS】ENS中的Namehash加密逻辑
- 2015 中国大数据技术大会 PPT 尝鲜
- 你被美国监控了,美国监控丑闻——棱镜项目事件
- 条件编译/条件宏定义
- BCGControlBarPro CBCGPGridCtrl单元格添加控件
- 单片机接收到红外对管的数据怎么用c语言程序传给led显示器,单片机实现遥控器-红外数据传输--红外编解码原理...
- 安徽三连学院计算机考试,安徽三联学院2017年3月计算机等级考试报名时间
- 联想ThinkPad E40安装XP的方法
- Android Support Library 学习入门
- Unity 之 UGUI Layout自动布局组件详解