Merge "Adjust camera preview area to square"

This commit is contained in:
Johnson Lu
2018-12-18 01:13:14 +00:00
committed by Android (Google) Code Review
7 changed files with 155 additions and 70 deletions

View File

@@ -18,7 +18,6 @@ package com.android.settings.wifi.dpp;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
@@ -30,7 +29,6 @@ import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.wifi.qrcode.QrDecorateView;
import com.android.settings.R;
/**
@@ -50,14 +48,8 @@ public abstract class WifiDppQrCodeBaseFragment extends InstrumentedFragment {
private TextView mTitle;
private TextView mDescription;
private SurfaceView mPreviewView; //optional, for WifiDppQrCodeScannerFragment
private QrDecorateView mDecorateViiew; //optional, for WifiDppQrCodeScannerFragment
private TextView mErrorMessage; //optional, for WifiDppQrCodeScannerFragment
private ImageView mBarcodeView; //optional, for WifiDppQrCodeGeneratorFragment
private ListView mSavedWifiNetworkList; //optional, for WifiDppChooseSavedWifiNetworkFragment
private ProgressBar mProgressBar; //optional, for WifiDppAddDeviceFragment
private ImageView mWifiApPictureView; //optional, for WifiDppAddDeviceFragment
private TextView mChooseDifferentNetwork;//optional, for WifiDppAddDeviceFragment
@@ -91,13 +83,8 @@ public abstract class WifiDppQrCodeBaseFragment extends InstrumentedFragment {
private void initView(View view) {
mTitle = view.findViewById(R.id.title);
mDescription = view.findViewById(R.id.description);
mPreviewView = view.findViewById(R.id.preview_view);
mDecorateViiew = view.findViewById(R.id.decorate_view);
mErrorMessage = view.findViewById(R.id.error_message);
mBarcodeView = view.findViewById(R.id.barcode_view);
mSavedWifiNetworkList = view.findViewById(R.id.saved_wifi_network_list);
mProgressBar = view.findViewById(R.id.progress_bar);

View File

@@ -21,14 +21,16 @@ import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Size;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
import android.view.View;
import com.android.settings.R;
@@ -36,10 +38,10 @@ import com.android.settings.wifi.qrcode.QrCamera;
import com.android.settings.wifi.qrcode.QrDecorateView;
public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment implements
SurfaceHolder.Callback,
SurfaceTextureListener,
QrCamera.ScannerCallback {
private QrCamera mCamera;
private SurfaceView mSurfaceView;
private TextureView mTextureView;
private QrDecorateView mDecorateView;
/** true if the fragment working for configurator, false enrollee*/
@@ -109,22 +111,12 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mSurfaceView = (SurfaceView) view.findViewById(R.id.preview_view);
final SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceHolder.addCallback(this);
mTextureView = (TextureView) view.findViewById(R.id.preview_view);
mTextureView.setSurfaceTextureListener(this);
mDecorateView = (QrDecorateView) view.findViewById(R.id.decorate_view);
}
@Override
public void onDestroyView() {
SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
surfaceHolder.removeCallback(this);
super.onDestroyView();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.removeItem(Menu.FIRST);
@@ -133,23 +125,29 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
}
@Override
public void surfaceCreated(final SurfaceHolder holder) {
initCamera(holder);
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
initCamera(surface);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Do nothing
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
destroyCamera();
return true;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Do nothing
}
@Override
public Size getViewSize() {
return new Size(mSurfaceView.getWidth(), mSurfaceView.getHeight());
return new Size(mTextureView.getWidth(), mTextureView.getHeight());
}
@Override
@@ -157,6 +155,11 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
return new Rect(0, 0, previewSize.getHeight(), previewSize.getHeight());
}
@Override
public void setTransform(Matrix transform) {
mTextureView.setTransform(transform);
}
@Override
public void handleSuccessfulResult(String qrCode) {
destroyCamera();
@@ -169,11 +172,11 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
destroyCamera();
}
private void initCamera(SurfaceHolder holder) {
private void initCamera(SurfaceTexture surface) {
// Check if the camera has already created.
if (mCamera == null) {
mCamera = new QrCamera(getContext(), this);
mCamera.start(holder);
mCamera.start(surface);
}
}

View File

@@ -17,7 +17,10 @@
package com.android.settings.wifi.qrcode;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
@@ -29,7 +32,6 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.WindowManager;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
@@ -50,7 +52,7 @@ import androidx.annotation.VisibleForTesting;
/**
* Manage the camera for the QR scanner and help the decoder to get the image inside the scanning
* frame. Caller prepares a {@link SurfaceHolder} then call {@link #start(SurfaceHolder)} to
* frame. Caller prepares a {@link SurfaceTexture} then call {@link #start(SurfaceTexture)} to
* start QR Code scanning. The scanning result will return by ScannerCallback interface. Caller
* can also call {@link #stop()} to halt QR Code scanning before the result returned.
*/
@@ -90,12 +92,11 @@ public class QrCamera extends Handler {
* The function start camera preview and capture pictures to decode QR code continuously in a
* background task.
*
* @param surfaceHolder the Surface to be used for live preview, must already contain a surface
* when this method is called.
* @param surface The surface to be used for live preview.
*/
public void start(SurfaceHolder surfaceHolder) {
public void start(SurfaceTexture surface) {
if (mDecodeTask == null) {
mDecodeTask = new DecodingTask(surfaceHolder);
mDecodeTask = new DecodingTask(surface);
// Execute in the separate thread pool to prevent block other AsyncTask.
mDecodeTask.executeOnExecutor(Executors.newSingleThreadExecutor());
}
@@ -144,6 +145,13 @@ public class QrCamera extends Handler {
* @return The rectangle would like to crop from the camera preview shot.
*/
Rect getFramePosition(Size previewSize, int cameraOrientation);
/**
* Sets the transform to associate with preview area.
*
* @param transform The transform to apply to the content of preview
*/
void setTransform(Matrix transform);
}
private void setCameraParameter() {
@@ -200,15 +208,15 @@ public class QrCamera extends Handler {
private class DecodingTask extends AsyncTask<Void, Void, String> {
private QrYuvLuminanceSource mImage;
private SurfaceHolder mSurfaceHolder;
private SurfaceTexture mSurface;
private DecodingTask(SurfaceHolder surfaceHolder) {
mSurfaceHolder = surfaceHolder;
private DecodingTask(SurfaceTexture surface) {
mSurface = surface;
}
@Override
protected String doInBackground(Void... tmp) {
if (!initCamera(mSurfaceHolder)) {
if (!initCamera(mSurface)) {
return null;
}
@@ -253,7 +261,7 @@ public class QrCamera extends Handler {
}
}
private boolean initCamera(SurfaceHolder surfaceHolder) {
private boolean initCamera(SurfaceTexture surface) {
final int numberOfCameras = Camera.getNumberOfCameras();
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
try {
@@ -261,7 +269,7 @@ public class QrCamera extends Handler {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
mCamera = Camera.open(i);
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.setPreviewTexture(surface);
mCameraOrientation = cameraInfo.orientation;
break;
}
@@ -272,6 +280,7 @@ public class QrCamera extends Handler {
return false;
}
setCameraParameter();
setTransformationMatrix(mScannerCallback.getViewSize());
if (!startPreview()) {
Log.e(TAG, "Error to init Camera");
mCamera = null;
@@ -288,6 +297,36 @@ public class QrCamera extends Handler {
}
}
/** Set transfom matrix to crop and center the preview picture */
private void setTransformationMatrix(Size viewSize) {
// Check aspect ratio, can only handle square view.
final int viewRatio = (int)getRatio(viewSize.getWidth(), viewSize.getHeight());
if (viewRatio != 1) {
throw new IllegalArgumentException("Preview area should be square");
}
final boolean isPortrait = mContext.get().getResources().getConfiguration().orientation
== Configuration.ORIENTATION_PORTRAIT ? true : false;
final int previewWidth = isPortrait ? mPreviewSize.getWidth() : mPreviewSize.getHeight();
final int previewHeight = isPortrait ? mPreviewSize.getHeight() : mPreviewSize.getWidth();
final float ratioPreview = (float) getRatio(previewWidth, previewHeight);
// Calculate transformation matrix.
float scaleX = 1.0f;
float scaleY = 1.0f;
if (previewWidth > previewHeight) {
scaleY = scaleX / ratioPreview;
} else {
scaleX = scaleY / ratioPreview;
}
// Set the transform matrix.
final Matrix matrix = new Matrix();
matrix.setScale(scaleX, scaleY);
mScannerCallback.setTransform(matrix);
}
private QrYuvLuminanceSource getFrameImage(byte[] imageData) {
final Rect frame = mScannerCallback.getFramePosition(mPreviewSize, mCameraOrientation);
final Camera.Size size = mParameters.getPictureSize();

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.wifi.qrcode;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.FrameLayout;
/**
* A customize square {@link FrameLayout}.
* This is used for camera preview. Choose the smaller size of both dimensions as length and width.
*/
public class QrPreviewLayout extends FrameLayout {
public QrPreviewLayout(Context context) {
super(context);
}
public QrPreviewLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public QrPreviewLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Choose the smaller size of the two dimensions.
if (MeasureSpec.getSize(widthMeasureSpec) > MeasureSpec.getSize(heightMeasureSpec)) {
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
} else {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
}
}