[go: nahoru, domu]

blob: ed43db711b93ae8b7a8bf906c5d63c72bf10023d [file] [log] [blame]
/*
* Copyright 2019 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 androidx.camera.testing.activity;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresPermission;
import androidx.annotation.VisibleForTesting;
import androidx.camera.testing.R;
import androidx.test.espresso.idling.CountingIdlingResource;
import java.util.Arrays;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/** An activity which opens the camera via Camera2 API for testing. */
public class Camera2TestActivity extends Activity {
private static final String TAG = "Camera2TestActivity";
private static final int FRAMES_UNTIL_VIEW_IS_READY = 5;
public static final String EXTRA_CAMERA_ID = "androidx.camera.cameraId";
/**
* A {@link Semaphore} to prevent the app from exiting before closing the camera.
*/
final Semaphore mCameraOpenCloseLock = new Semaphore(1);
@Nullable
CameraDevice mCameraDevice;
@Nullable
CameraCaptureSession mCaptureSession;
@Nullable
Handler mBackgroundHandler;
@Nullable
CaptureRequest.Builder mPreviewRequestBuilder;
private TextureView mTextureView;
@Nullable
private String mCameraId;
@Nullable
private HandlerThread mBackgroundThread;
private final TextureView.SurfaceTextureListener mSurfaceTextureListener =
new TextureView.SurfaceTextureListener() {
@SuppressLint("MissingPermission")
@Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width,
int height) {
openCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width,
int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
// Wait until surface texture receives enough updates.
if (!mPreviewReady.isIdleNow()) {
mPreviewReady.decrement();
}
}
};
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
Log.d(TAG, "Camera onOpened: " + cameraDevice);
// This method is called when the camera is opened. We start camera preview here.
mCameraOpenCloseLock.release();
mCameraDevice = cameraDevice;
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
Log.d(TAG, "Camera onDisconnected: " + cameraDevice);
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
finish();
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int error) {
Log.d(TAG, "Camera onError: " + cameraDevice);
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
finish();
}
};
@VisibleForTesting
public final CountingIdlingResource mPreviewReady = new CountingIdlingResource("PreviewReady");
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera_main);
mTextureView = findViewById(R.id.textureView);
mCameraId = getIntent().getStringExtra(EXTRA_CAMERA_ID);
if (TextUtils.isEmpty(mCameraId)) {
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
// Use the first camera available.
String[] cameraIds = new String[0];
try {
cameraIds = manager.getCameraIdList();
} catch (CameraAccessException e) {
Log.d(TAG, "Cannot find default camera id");
}
if (cameraIds.length > 0) {
mCameraId = cameraIds[0];
}
}
}
@SuppressLint("MissingPermission")
@Override
public void onResume() {
super.onResume();
startBackgroundThread();
for (int i = 0; i < FRAMES_UNTIL_VIEW_IS_READY; i++) {
mPreviewReady.increment();
}
// When the screen is turned off and turned back on, the SurfaceTexture is already
// available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
// a camera and start preview from here (otherwise, we wait until the surface is ready in
// the SurfaceTextureListener).
if (mTextureView.isAvailable()) {
openCamera();
} else {
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
}
@Override
public void onPause() {
closeCamera();
stopBackgroundThread();
super.onPause();
}
@RequiresPermission(Manifest.permission.CAMERA)
void openCamera() {
if (TextUtils.isEmpty(mCameraId)) {
Log.d(TAG, "Cannot open the camera");
return;
}
Log.d(TAG, "Opening camera: " + mCameraId);
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}
/**
* Closes the current {@link CameraDevice}.
*/
void closeCamera() {
try {
mCameraOpenCloseLock.acquire();
if (null != mCaptureSession) {
mCaptureSession.close();
mCaptureSession = null;
}
if (null != mCameraDevice) {
mCameraDevice.close();
mCameraDevice = null;
}
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
} finally {
mCameraOpenCloseLock.release();
}
}
/**
* Creates a new {@link CameraCaptureSession} for camera preview.
*/
void createCameraPreviewSession() {
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
// We configure the size of default buffer to be the size of camera preview we want.
texture.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight());
// This is the output Surface we need to start preview.
Surface surface = new Surface(texture);
// We set up a CaptureRequest.Builder with the output Surface.
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
// Here, we create a CameraCaptureSession for camera preview.
mCameraDevice.createCaptureSession(Arrays.asList(surface),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(
@NonNull CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (null == mCameraDevice) {
return;
}
// When the session is ready, we start displaying the preview.
mCaptureSession = cameraCaptureSession;
try {
// Auto focus should be continuous for camera preview.
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// Finally, we start displaying the camera preview.
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
new CameraCaptureSession.CaptureCallback() {
},
mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(
@NonNull CameraCaptureSession cameraCaptureSession) {
}
}, null
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
* Starts a background thread and its {@link Handler}.
*/
private void startBackgroundThread() {
mBackgroundThread = new HandlerThread("CameraBackground");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
/**
* Stops the background thread and its {@link Handler}.
*/
private void stopBackgroundThread() {
mBackgroundThread.quitSafely();
try {
mBackgroundThread.join();
mBackgroundThread = null;
mBackgroundHandler = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}