From f505d3a61f89cdcd1e365c5daada8f856e9d844e Mon Sep 17 00:00:00 2001 From: Mehedi Date: Fri, 21 Oct 2016 11:47:26 +0600 Subject: [PATCH 1/2] preview will scale to fill available space. --- mvbarcodereader/build.gradle | 4 +- .../BarcodeCaptureFragment.java | 34 +++----- .../mvbarcodereader/camera/CameraSource.java | 87 ++++++++----------- .../camera/CameraSourcePreview.java | 57 +++++++++--- 4 files changed, 96 insertions(+), 86 deletions(-) diff --git a/mvbarcodereader/build.gradle b/mvbarcodereader/build.gradle index 0279718..b52df50 100644 --- a/mvbarcodereader/build.gradle +++ b/mvbarcodereader/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' apply plugin: "com.jfrog.bintray" -version = "0.1.0" +version = "0.1.1" android { compileSdkVersion 24 @@ -11,7 +11,7 @@ android { defaultConfig { minSdkVersion 10 targetSdkVersion 24 - versionCode 1 + versionCode 2 versionName version } buildTypes { diff --git a/mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/BarcodeCaptureFragment.java b/mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/BarcodeCaptureFragment.java index 26855a0..7d8b920 100644 --- a/mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/BarcodeCaptureFragment.java +++ b/mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/BarcodeCaptureFragment.java @@ -136,24 +136,6 @@ public void onClick(View v) { gestureDetector = new GestureDetector(getActivity(), new CaptureGestureListener()); scaleGestureDetector = new ScaleGestureDetector(getActivity(), new ScaleListener()); - topLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - topLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this); - - if (mCameraSource == null) { - - int rc = ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA); - if (rc == PackageManager.PERMISSION_GRANTED) { - createCameraSource(); - startCameraSource(); - } else { - requestCameraPermission(); - } - } else startCameraSource(); - } - }); - content.setOnTouchListener(this); return content; } @@ -162,9 +144,15 @@ public void onGlobalLayout() { public void onResume() { super.onResume(); - if (mCameraSource != null) { - startCameraSource(); - } + if (mCameraSource == null) { + int rc = ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA); + if (rc == PackageManager.PERMISSION_GRANTED) { + createCameraSource(); + startCameraSource(); + } else { + requestCameraPermission(); + } + } else startCameraSource(); } /** @@ -344,15 +332,13 @@ else if (mGraphicOverlay.getFirstGraphic() != null && mGraphicOverlay.getFirstGr } } - boolean isPortrait = mPreview.isPortraitMode(); + //boolean isPortrait = mPreview.isPortraitMode(); // Creates and starts the camera. Note that this uses a higher resolution in comparison // to other detection examples to enable the barcode detector to detect small barcodes // at long distances. CameraSource.Builder builder = new CameraSource.Builder(getActivity().getApplicationContext(), barcodeDetector) .setFacing(CameraSource.CAMERA_FACING_BACK) - .setRequestedPreviewSize(isPortrait ? topLayout.getHeight() : topLayout.getWidth(), - isPortrait ? topLayout.getWidth() : topLayout.getHeight()) .setRequestedFps(15.0f); // make sure that auto focus is an available option diff --git a/mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/camera/CameraSource.java b/mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/camera/CameraSource.java index f612db0..600cc5c 100644 --- a/mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/camera/CameraSource.java +++ b/mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/camera/CameraSource.java @@ -44,6 +44,8 @@ import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -129,15 +131,13 @@ public class CameraSource { * See {@link Frame.Metadata#getRotation()}. */ private int mRotation; + private int mCameraId; private Size mPreviewSize; // These values may be requested by the caller. Due to hardware limitations, we may need to // select close, but not exactly the same values for these. private float mRequestedFps = 30.0f; - private int mRequestedPreviewWidth = 1024; - private int mRequestedPreviewHeight = 768; - private String mFocusMode = null; private String mFlashMode = null; @@ -155,6 +155,8 @@ public class CameraSource { private Thread mProcessingThread; private FrameProcessingRunnable mFrameProcessor; + private boolean isSafeToTakePicture = false; + /** * Map to convert between a byte array, received from the camera, and its associated byte * buffer. We use byte buffers internally because this is a more efficient way to call into @@ -211,25 +213,6 @@ public Builder setFlashMode(@FlashMode String mode) { return this; } - /** - * Sets the desired width and height of the camera frames in pixels. If the exact desired - * values are not available options, the best matching available options are selected. - * Also, we try to select a preview size which corresponds to the aspect ratio of an - * associated full picture size, if applicable. Default: 1024x768. - */ - public Builder setRequestedPreviewSize(int width, int height) { - // Restrict the requested range to something within the realm of possibility. The - // choice of 1000000 is a bit arbitrary -- intended to be well beyond resolutions that - // devices can support. We bound this to avoid int overflow in the code later. - final int MAX = 1000000; - if ((width <= 0) || (width > MAX) || (height <= 0) || (height > MAX)) { - throw new IllegalArgumentException("Invalid preview size: " + width + "x" + height); - } - mCameraSource.mRequestedPreviewWidth = width; - mCameraSource.mRequestedPreviewHeight = height; - return this; - } - /** * Sets the camera to use (either {@link #CAMERA_FACING_BACK} or * {@link #CAMERA_FACING_FRONT}). Default: back facing. @@ -353,6 +336,7 @@ public CameraSource start() throws IOException { mCamera.setPreviewDisplay(mDummySurfaceView.getHolder()); } mCamera.startPreview(); + isSafeToTakePicture = true; mProcessingThread = new Thread(mFrameProcessor); mFrameProcessor.setActive(true); @@ -378,6 +362,7 @@ public CameraSource start(SurfaceHolder surfaceHolder) throws IOException { mCamera = createCamera(); mCamera.setPreviewDisplay(surfaceHolder); mCamera.startPreview(); + isSafeToTakePicture = true; mProcessingThread = new Thread(mFrameProcessor); mFrameProcessor.setActive(true); @@ -412,6 +397,7 @@ public void stop() { // clear the buffer to prevent oom exceptions mBytesToByteBuffer.clear(); + isSafeToTakePicture = false; if (mCamera != null) { mCamera.stopPreview(); @@ -496,7 +482,8 @@ public int doZoom(float scale) { */ public void takePicture(ShutterCallback shutter, PictureCallback jpeg) { synchronized (mCameraLock) { - if (mCamera != null) { + if (mCamera != null && isSafeToTakePicture) { + isSafeToTakePicture = false; PictureStartCallback startCallback = new PictureStartCallback(); startCallback.mDelegate = shutter; PictureDoneCallback doneCallback = new PictureDoneCallback(); @@ -701,6 +688,7 @@ public void onPictureTaken(byte[] data, Camera camera) { synchronized (mCameraLock) { if (mCamera != null) { mCamera.startPreview(); + isSafeToTakePicture = true; } } } @@ -742,13 +730,13 @@ public void onAutoFocusMoving(boolean start, Camera camera) { */ @SuppressLint("InlinedApi") private Camera createCamera() { - int requestedCameraId = getIdForRequestedCamera(mFacing); - if (requestedCameraId == -1) { + mCameraId = getIdForRequestedCamera(mFacing); + if (mCameraId == -1) { throw new RuntimeException("Could not find requested camera."); } - Camera camera = Camera.open(requestedCameraId); + Camera camera = Camera.open(mCameraId); - SizePair sizePair = selectSizePair(camera, mRequestedPreviewWidth, mRequestedPreviewHeight); + SizePair sizePair = selectSizePair(camera); if (sizePair == null) { throw new RuntimeException("Could not find suitable preview size."); } @@ -772,7 +760,7 @@ private Camera createCamera() { previewFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]); parameters.setPreviewFormat(ImageFormat.NV21); - setRotation(camera, parameters, requestedCameraId); + setRotation(camera, parameters, mCameraId); if (mFocusMode != null) { if (parameters.getSupportedFocusModes().contains( @@ -842,29 +830,22 @@ private static int getIdForRequestedCamera(int facing) { * image. * * @param camera the camera to select a preview size from - * @param desiredWidth the desired width of the camera preview frames - * @param desiredHeight the desired height of the camera preview frames * @return the selected preview and picture size pair */ - private static SizePair selectSizePair(Camera camera, int desiredWidth, int desiredHeight) { + private static SizePair selectSizePair(Camera camera) { List validPreviewSizes = generateValidPreviewSizeList(camera); - // The method for selecting the best size is to minimize the sum of the differences between - // the desired values and the actual values for width and height. This is certainly not the - // only way to select the best size, but it provides a decent tradeoff between using the - // closest aspect ratio vs. using the closest pixel area. - SizePair selectedPair = null; - int minDiff = Integer.MAX_VALUE; - for (SizePair sizePair : validPreviewSizes) { - Size size = sizePair.previewSize(); - int diff = Math.abs(size.getWidth() - desiredWidth) + - Math.abs(size.getHeight() - desiredHeight); - if (diff < minDiff) { - selectedPair = sizePair; - minDiff = diff; + Collections.sort(validPreviewSizes, new Comparator() { + @Override + public int compare(SizePair lhs, SizePair rhs) { + return (rhs.previewSize().getHeight() * rhs.previewSize().getWidth()) + - (lhs.previewSize().getHeight() * lhs.previewSize().getWidth()); } - } + }); + SizePair selectedPair = validPreviewSizes.get(0); + Log.d(TAG, "selected preview size: w:" + selectedPair.previewSize().getWidth() + + ", h:" + selectedPair.previewSize().getHeight()); return selectedPair; } @@ -907,18 +888,18 @@ public Size pictureSize() { */ private static List generateValidPreviewSizeList(Camera camera) { Camera.Parameters parameters = camera.getParameters(); - List supportedPreviewSizes = + List supportedPreviewSizes = parameters.getSupportedPreviewSizes(); - List supportedPictureSizes = + List supportedPictureSizes = parameters.getSupportedPictureSizes(); List validPreviewSizes = new ArrayList<>(); - for (Camera.Size previewSize : supportedPreviewSizes) { + for (android.hardware.Camera.Size previewSize : supportedPreviewSizes) { float previewAspectRatio = (float) previewSize.width / (float) previewSize.height; // By looping through the picture sizes in order, we favor the higher resolutions. // We choose the highest resolution in order to support taking the full resolution // picture later. - for (Camera.Size pictureSize : supportedPictureSizes) { + for (android.hardware.Camera.Size pictureSize : supportedPictureSizes) { float pictureAspectRatio = (float) pictureSize.width / (float) pictureSize.height; if (Math.abs(previewAspectRatio - pictureAspectRatio) < ASPECT_RATIO_TOLERANCE) { validPreviewSizes.add(new SizePair(previewSize, pictureSize)); @@ -932,7 +913,7 @@ private static List generateValidPreviewSizeList(Camera camera) { // still account for it. if (validPreviewSizes.size() == 0) { Log.w(TAG, "No preview sizes have a corresponding same-aspect-ratio picture size"); - for (Camera.Size previewSize : supportedPreviewSizes) { + for (android.hardware.Camera.Size previewSize : supportedPreviewSizes) { // The null picture size will let us know that we shouldn't set a picture size. validPreviewSizes.add(new SizePair(previewSize, null)); } @@ -974,6 +955,12 @@ private int[] selectPreviewFpsRange(Camera camera, float desiredPreviewFps) { return selectedFpsRange; } + public void updateRotation() { + if (mCamera != null) { + setRotation(mCamera, mCamera.getParameters(), mCameraId); + } + } + /** * Calculates the correct rotation for the given camera id and sets the rotation in the * parameters. It also sets the camera's display orientation and rotation. diff --git a/mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/camera/CameraSourcePreview.java b/mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/camera/CameraSourcePreview.java index 1cb4f96..cd4b33f 100644 --- a/mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/camera/CameraSourcePreview.java +++ b/mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/camera/CameraSourcePreview.java @@ -128,10 +128,22 @@ public void surfaceChanged(SurfaceHolder holder, int format, int width, int heig } } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + if (mCameraSource != null) mCameraSource.updateRotation(); + } + + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - int width = 320; - int height = 240; + final int layoutWidth = right - left; + final int layoutHeight = bottom - top; + + int width = layoutWidth; + int height = layoutHeight; + if (mCameraSource != null) { Size size = mCameraSource.getPreviewSize(); if (size != null) { @@ -148,19 +160,44 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto height = tmp; } - final int layoutWidth = right - left; - final int layoutHeight = bottom - top; + final float aspectRatio = (float) width / (float) height; - // Computes height and width for potentially doing fit width. - int childWidth = layoutWidth; - int childHeight = (int) (((float) layoutWidth / (float) width) * height); + Log.d(TAG, "aspect ratio: " + aspectRatio); - // If height is too tall using fit width, does fit height instead. - if (childHeight > layoutHeight) { + int childWidth; + int childHeight; + + if (layoutHeight > layoutWidth) { + //fit height childHeight = layoutHeight; - childWidth = (int) (((float) layoutHeight / (float) height) * width); + childWidth = Math.round(childHeight * aspectRatio); + Log.d(TAG, "fit height -> cw: " + childWidth + ", ch: " + childHeight); + + if (childWidth < layoutWidth) { + int diff = layoutWidth - childWidth; + childWidth = childWidth + diff; + childHeight = childHeight + Math.round(diff / aspectRatio); + + Log.d(TAG, "fit height [nested block] -> cw: " + childWidth + ", ch: " + childHeight); + } + } else { + //fit width + childWidth = layoutWidth; + childHeight = Math.round(childWidth / aspectRatio); + Log.d(TAG, "fit width -> cw: " + childWidth + ", ch: " + childHeight); + + if (childHeight < layoutHeight) { + int diff = layoutHeight - childHeight; + childHeight = childHeight + diff; + childWidth = childWidth + Math.round(diff * aspectRatio); + + Log.d(TAG, "fit width [nested block] -> cw: " + childWidth + ", ch: " + childHeight); + } } + Log.d(TAG, "layout size: w: " + layoutWidth + ", h: " + layoutHeight + + " - fit size: w: " + childWidth + ", h: " + childHeight); + for (int i = 0; i < getChildCount(); ++i) { getChildAt(i).layout(0, 0, childWidth, childHeight); } From b7c813b70714151f636965918b6f7d8e21a40633 Mon Sep 17 00:00:00 2001 From: Mehedi Date: Fri, 21 Oct 2016 17:50:51 +0600 Subject: [PATCH 2/2] bumped the library version --- app/build.gradle | 2 +- mvbarcodereader/build.gradle | 4 ++-- .../src/main/res/layout/barcode_capture_activity.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 13baba9..e6c932b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,7 +8,7 @@ android { applicationId "devliving.online.mvbarcodereadersample" minSdkVersion 10 targetSdkVersion 24 - versionCode 1 + versionCode 2 versionName "1.0" } buildTypes { diff --git a/mvbarcodereader/build.gradle b/mvbarcodereader/build.gradle index b52df50..8abb08e 100644 --- a/mvbarcodereader/build.gradle +++ b/mvbarcodereader/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' apply plugin: "com.jfrog.bintray" -version = "0.1.1" +version = "1.0.0" android { compileSdkVersion 24 @@ -11,7 +11,7 @@ android { defaultConfig { minSdkVersion 10 targetSdkVersion 24 - versionCode 2 + versionCode 3 versionName version } buildTypes { diff --git a/mvbarcodereader/src/main/res/layout/barcode_capture_activity.xml b/mvbarcodereader/src/main/res/layout/barcode_capture_activity.xml index 934416d..e3eea8c 100644 --- a/mvbarcodereader/src/main/res/layout/barcode_capture_activity.xml +++ b/mvbarcodereader/src/main/res/layout/barcode_capture_activity.xml @@ -1,7 +1,7 @@ \ No newline at end of file