Merge "Adjust camera preview area to square"
diff --git a/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml b/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml
index 0c938f8..fb50def 100644
--- a/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml
+++ b/res/layout-land/wifi_dpp_qrcode_scanner_fragment.xml
@@ -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"
diff --git a/res/layout/wifi_dpp_qrcode_scanner_fragment.xml b/res/layout/wifi_dpp_qrcode_scanner_fragment.xml
index 0beacff..913998f 100644
--- a/res/layout/wifi_dpp_qrcode_scanner_fragment.xml
+++ b/res/layout/wifi_dpp_qrcode_scanner_fragment.xml
@@ -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"
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
index e6427d9..cfdb40a 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
@@ -18,7 +18,6 @@
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 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 @@
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 @@
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);
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
index 342e693..e7124a5 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
@@ -21,14 +21,16 @@
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.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,23 +111,13 @@
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 @@
}
@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
@@ -158,6 +156,11 @@
}
@Override
+ public void setTransform(Matrix transform) {
+ mTextureView.setTransform(transform);
+ }
+
+ @Override
public void handleSuccessfulResult(String qrCode) {
destroyCamera();
mDecorateView.setFocused(true);
@@ -169,11 +172,11 @@
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);
}
}
diff --git a/src/com/android/settings/wifi/qrcode/QrCamera.java b/src/com/android/settings/wifi/qrcode/QrCamera.java
index dc650b9..c60c30e 100644
--- a/src/com/android/settings/wifi/qrcode/QrCamera.java
+++ b/src/com/android/settings/wifi/qrcode/QrCamera.java
@@ -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.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 @@
/**
* 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 @@
* 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 @@
* @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 @@
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 @@
}
}
- 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 @@
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 @@
return false;
}
setCameraParameter();
+ setTransformationMatrix(mScannerCallback.getViewSize());
if (!startPreview()) {
Log.e(TAG, "Error to init Camera");
mCamera = null;
@@ -288,6 +297,36 @@
}
}
+ /** 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();
diff --git a/src/com/android/settings/wifi/qrcode/QrPreviewLayout.java b/src/com/android/settings/wifi/qrcode/QrPreviewLayout.java
new file mode 100644
index 0000000..56566ae
--- /dev/null
+++ b/src/com/android/settings/wifi/qrcode/QrPreviewLayout.java
@@ -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);
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java b/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java
index e32ac6b..0ef0273 100644
--- a/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/qrcode/QrCameraTest.java
@@ -22,9 +22,11 @@
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 @@
public class QrCameraTest {
@Mock
- private SurfaceHolder mSurfaceHolder;
+ private SurfaceTexture mSurfaceTexture;
private QrCamera mCamera;
private Context mContext;
@@ -78,6 +80,11 @@
mCameraCallbacked = true;
mCallbackSignal.countDown();
}
+
+ @Override
+ public void setTransform(Matrix transform) {
+ // Do nothing
+ }
}
private ScannerTestCallback mScannerCallback;
@@ -87,7 +94,7 @@
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 @@
@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();
}