From 352d866af767e3cc348aed1e0db9f9578e5e6b1b Mon Sep 17 00:00:00 2001 From: Arc Wang Date: Wed, 22 May 2019 16:04:28 +0800 Subject: [PATCH] Fix Wi-Fi QR scanner crash problem 1. Should only set picture size from the list of supported picture sizes. To choose the best picture size, we should keep ratio as preview size to avoid image distortion and QR code scan failure. 2. When Camera#setOneShotPreviewCallback callbacks, the image data is of preview size, not picture size. Bug: 132866467 Test: QrCameraTest manual Change-Id: I087e5fcdf064f111e915e0872b50ca5a45ff1d26 --- .../settings/wifi/qrcode/QrCamera.java | 58 +++++++++++++++++-- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/wifi/qrcode/QrCamera.java b/src/com/android/settings/wifi/qrcode/QrCamera.java index 6088fa597ac..c6825879558 100644 --- a/src/com/android/settings/wifi/qrcode/QrCamera.java +++ b/src/com/android/settings/wifi/qrcode/QrCamera.java @@ -62,7 +62,14 @@ public class QrCamera extends Handler { private static final int MSG_AUTO_FOCUS = 1; - private static double MIN_RATIO_DIFF_PERCENT = 0.1; + /** + * The max allowed difference between picture size ratio and preview size ratio. + * Uses to filter the picture sizes of similar preview size ratio, for example, if a preview + * size is 1920x1440, MAX_RATIO_DIFF 0.1 could allow picture size of 720x480 or 352x288 or + * 176x44 but not 1920x1080. + */ + private static double MAX_RATIO_DIFF = 0.1; + private static long AUTOFOCUS_INTERVAL_MS = 1500L; private static Map> HINTS = new ArrayMap<>(); @@ -168,7 +175,8 @@ public class QrCamera extends Handler { mParameters = mCamera.getParameters(); mPreviewSize = getBestPreviewSize(mParameters); mParameters.setPreviewSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); - mParameters.setPictureSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); + Size pictureSize = getBestPictureSize(mParameters); + mParameters.setPreviewSize(pictureSize.getWidth(), pictureSize.getHeight()); if (mParameters.getSupportedFlashModes().contains(Parameters.FLASH_MODE_OFF)) { mParameters.setFlashMode(Parameters.FLASH_MODE_OFF); @@ -338,8 +346,8 @@ public class QrCamera extends Handler { private QrYuvLuminanceSource getFrameImage(byte[] imageData) { final Rect frame = mScannerCallback.getFramePosition(mPreviewSize, mCameraOrientation); - final Camera.Size size = mParameters.getPictureSize(); - QrYuvLuminanceSource image = new QrYuvLuminanceSource(imageData, size.width, size.height); + final QrYuvLuminanceSource image = new QrYuvLuminanceSource(imageData, + mPreviewSize.getWidth(), mPreviewSize.getHeight()); return (QrYuvLuminanceSource) image.crop(frame.left, frame.top, frame.width(), frame.height()); } @@ -379,6 +387,48 @@ public class QrCamera extends Handler { return bestChoice; } + /** Get best picture size from the list of camera supported picture sizes. Compares the + * picture size and aspect ratio to choose the best one. */ + private Size getBestPictureSize(Camera.Parameters parameters) { + final Camera.Size previewSize = parameters.getPreviewSize(); + final double previewRatio = getRatio(previewSize.width, previewSize.height); + List bestChoices = new ArrayList<>(); + final List similarChoices = new ArrayList<>(); + + // Filter by ratio + for (Camera.Size size : parameters.getSupportedPictureSizes()) { + double ratio = getRatio(size.width, size.height); + if (ratio == previewRatio) { + bestChoices.add(new Size(size.width, size.height)); + } else if (Math.abs(ratio - previewRatio) < MAX_RATIO_DIFF) { + similarChoices.add(new Size(size.width, size.height)); + } + } + + if (bestChoices.size() == 0 && similarChoices.size() == 0) { + Log.d(TAG, "No proper picture size, return default picture size"); + Camera.Size defaultPictureSize = parameters.getPictureSize(); + return new Size(defaultPictureSize.width, defaultPictureSize.height); + } + + if (bestChoices.size() == 0) { + bestChoices = similarChoices; + } + + // Get the best by area + int bestAreaDifference = Integer.MAX_VALUE; + Size bestChoice = null; + final int previewArea = previewSize.width * previewSize.height; + for (Size size : bestChoices) { + int areaDifference = Math.abs(size.getWidth() * size.getHeight() - previewArea); + if (areaDifference < bestAreaDifference) { + bestAreaDifference = areaDifference; + bestChoice = size; + } + } + return bestChoice; + } + private double getRatio(double x, double y) { return (x < y) ? x / y : y / x; }