Merge "Adjust camera preview area to square"
This commit is contained in:
@@ -24,20 +24,19 @@
|
||||
|
||||
<include layout="@layout/wifi_dpp_fragment_header"/>
|
||||
|
||||
<FrameLayout
|
||||
<com.android.settings.wifi.qrcode.QrPreviewLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<SurfaceView
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center">
|
||||
<TextureView
|
||||
android:id="@+id/preview_view"
|
||||
android:layout_width="426dp"
|
||||
android:layout_height="320dp"
|
||||
android:layout_gravity="center"/>
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"/>
|
||||
<com.android.settings.wifi.qrcode.QrDecorateView
|
||||
android:id="@+id/decorate_view"
|
||||
android:layout_width="426dp"
|
||||
android:layout_height="320dp"
|
||||
android:layout_gravity="center"/>
|
||||
</FrameLayout>
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"/>
|
||||
</com.android.settings.wifi.qrcode.QrPreviewLayout>
|
||||
|
||||
<TextView android:id="@+id/error_message"
|
||||
android:layout_width="wrap_content"
|
||||
|
@@ -24,20 +24,20 @@
|
||||
|
||||
<include layout="@layout/wifi_dpp_fragment_header"/>
|
||||
|
||||
<FrameLayout
|
||||
<com.android.settings.wifi.qrcode.QrPreviewLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<SurfaceView
|
||||
android:layout_height="match_parent">
|
||||
<TextureView
|
||||
android:id="@+id/preview_view"
|
||||
android:layout_width="320dp"
|
||||
android:layout_height="426dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"/>
|
||||
<com.android.settings.wifi.qrcode.QrDecorateView
|
||||
android:id="@+id/decorate_view"
|
||||
android:layout_width="320dp"
|
||||
android:layout_height="426dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"/>
|
||||
</FrameLayout>
|
||||
</com.android.settings.wifi.qrcode.QrPreviewLayout>
|
||||
|
||||
<TextView android:id="@+id/error_message"
|
||||
android:layout_width="wrap_content"
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
|
50
src/com/android/settings/wifi/qrcode/QrPreviewLayout.java
Normal file
50
src/com/android/settings/wifi/qrcode/QrPreviewLayout.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -22,9 +22,11 @@ import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.util.Size;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
@@ -48,7 +50,7 @@ import org.robolectric.RuntimeEnvironment;
|
||||
public class QrCameraTest {
|
||||
|
||||
@Mock
|
||||
private SurfaceHolder mSurfaceHolder;
|
||||
private SurfaceTexture mSurfaceTexture;
|
||||
|
||||
private QrCamera mCamera;
|
||||
private Context mContext;
|
||||
@@ -78,6 +80,11 @@ public class QrCameraTest {
|
||||
mCameraCallbacked = true;
|
||||
mCallbackSignal.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransform(Matrix transform) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
private ScannerTestCallback mScannerCallback;
|
||||
@@ -87,7 +94,7 @@ public class QrCameraTest {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mScannerCallback = new ScannerTestCallback();
|
||||
mCamera = new QrCamera(mContext, mScannerCallback);
|
||||
mSurfaceHolder = mock(SurfaceHolder.class);
|
||||
mSurfaceTexture = mock(SurfaceTexture.class);
|
||||
mQrCode = "";
|
||||
mCameraCallbacked = false;
|
||||
mCallbackSignal = null;
|
||||
@@ -96,7 +103,7 @@ public class QrCameraTest {
|
||||
@Test
|
||||
public void testCamera_Init_Callback() throws InterruptedException {
|
||||
mCallbackSignal = new CountDownLatch(1);
|
||||
mCamera.start(mSurfaceHolder);
|
||||
mCamera.start(mSurfaceTexture);
|
||||
mCallbackSignal.await(5000, TimeUnit.MILLISECONDS);
|
||||
assertThat(mCameraCallbacked).isTrue();
|
||||
}
|
||||
|
Reference in New Issue
Block a user