java.lang.Object
↳FrameLayout
↳androidx.camera.view.CameraView
Overview
A View
that displays a preview of the camera with methods CameraView,
CameraView,
CameraView.startRecording(File, Executor, OnVideoSavedCallback)
and CameraView.stopRecording().
Because the Camera is a limited resource and consumes a high amount of power, CameraView must
be opened/closed. CameraView will handle opening/closing automatically through use of a LifecycleOwner. Use CameraView.bindToLifecycle(LifecycleOwner) to start the camera.
Summary
Constructors |
---|
public | CameraView(Context context)
|
public | CameraView(Context context, AttributeSet attrs)
|
public | CameraView(Context context, AttributeSet attrs, int defStyle)
|
public | CameraView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
|
Methods |
---|
public void | bindToLifecycle(LifecycleOwner lifecycleOwner)
Binds control of the camera used by this view to the given lifecycle. |
public void | enableTorch(boolean torch)
Turns on/off torch. |
protected LayoutParams | generateDefaultLayoutParams()
|
public java.lang.Integer | getCameraLensFacing()
Returns the currently selected lensFacing. |
public CameraView.CaptureMode | getCaptureMode()
Returns the scale type used to scale the preview. |
public int | getFlash()
Gets the active flash strategy. |
public long | getMaxVideoDuration()
Returns the maximum duration of videos, or CameraView.INDEFINITE_VIDEO_DURATION if there is no
timeout. |
public float | getMaxZoomRatio()
Returns the maximum zoom ratio. |
public float | getMinZoomRatio()
Returns the minimum zoom ratio. |
public LiveData<PreviewView.StreamState> | getPreviewStreamState()
Gets the LiveData of the underlying PreviewView's
PreviewView.StreamState. |
public PreviewView.ScaleType | getScaleType()
Returns the scale type used to scale the preview. |
public float | getZoomRatio()
Returns the current zoom ratio. |
public boolean | hasCameraWithLensFacing(int lensFacing)
Queries whether the current device has a camera with the specified direction. |
public boolean | isPinchToZoomEnabled()
Returns whether the view allows pinch-to-zoom. |
public boolean | isRecording()
|
public boolean | isTorchOn()
Returns current torch status. |
public boolean | isZoomSupported()
Returns whether the bound camera supports zooming. |
protected void | onAttachedToWindow()
|
protected void | onDetachedFromWindow()
|
protected void | onLayout(boolean changed, int left, int top, int right, int bottom)
|
protected void | onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
protected void | onRestoreInstanceState(Parcelable savedState)
|
protected Parcelable | onSaveInstanceState()
|
public boolean | onTouchEvent(MotionEvent event)
|
public boolean | performClick()
Focus the position of the touch event, or focus the center of the preview for
accessibility events |
public void | setCameraLensFacing(java.lang.Integer lensFacing)
Sets the desired camera by specifying desired lensFacing. |
public void | setCaptureMode(CameraView.CaptureMode captureMode)
Sets the CameraView capture mode |
public void | setFlash(int flashMode)
Sets the active flash strategy. |
public void | setPinchToZoomEnabled(boolean enabled)
Sets whether the view should allow pinch-to-zoom. |
public void | setScaleType(PreviewView.ScaleType scaleType)
Sets the view finder scale type. |
public void | setZoomRatio(float zoomRatio)
Sets the current zoom ratio. |
public void | startRecording(java.io.File file, java.util.concurrent.Executor executor, OnVideoSavedCallback callback)
Takes a video and calls the OnVideoSavedCallback when done. |
public void | startRecording(ParcelFileDescriptor fd, java.util.concurrent.Executor executor, OnVideoSavedCallback callback)
Takes a video and calls the OnVideoSavedCallback when done. |
public void | stopRecording()
Stops an in progress video. |
public void | takePicture(java.util.concurrent.Executor executor, ImageCapture.OnImageCapturedCallback callback)
Takes a picture, and calls ImageCapture.OnImageCapturedCallback.onCaptureSuccess(ImageProxy)
once when done. |
public void | takePicture(ImageCapture.OutputFileOptions outputFileOptions, java.util.concurrent.Executor executor, ImageCapture.OnImageSavedCallback callback)
Takes a picture and calls
ImageCapture.OnImageSavedCallback.onImageSaved(ImageCapture.OutputFileResults) when done. |
public void | toggleCamera()
Toggles between the primary front facing camera and the primary back facing camera. |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Constructors
public
CameraView(Context context)
public
CameraView(Context context, AttributeSet attrs)
public
CameraView(Context context, AttributeSet attrs, int defStyle)
public
CameraView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
Methods
Binds control of the camera used by this view to the given lifecycle.
This links opening/closing the camera to the given lifecycle. The camera will not operate
unless this method is called with a valid LifecycleOwner that is not in the Lifecycle.State.DESTROYED state. Call this method only once camera
permissions have been obtained.
Once the provided lifecycle has transitioned to a Lifecycle.State.DESTROYED state, CameraView must be bound to a new
lifecycle through this method in order to operate the camera.
Parameters:
lifecycleOwner: The lifecycle that will control this view's camera
protected LayoutParams
generateDefaultLayoutParams()
protected Parcelable
onSaveInstanceState()
protected void
onRestoreInstanceState(Parcelable savedState)
protected void
onAttachedToWindow()
protected void
onDetachedFromWindow()
public
LiveData<PreviewView.StreamState>
getPreviewStreamState()
Gets the LiveData of the underlying PreviewView's
PreviewView.StreamState.
Returns:
A LiveData containing the PreviewView.StreamState. Apps can either
get current value by LiveData.getValue() or register a observer by
LiveData.observe(LifecycleOwner, Observer super T>).
See also: PreviewView.getPreviewStreamState()
protected void
onMeasure(int widthMeasureSpec, int heightMeasureSpec)
protected void
onLayout(boolean changed, int left, int top, int right, int bottom)
Returns the scale type used to scale the preview.
Returns:
The current PreviewView.ScaleType.
Sets the view finder scale type.
This controls how the view finder should be scaled and positioned within the view.
Parameters:
scaleType: The desired PreviewView.ScaleType.
Returns the scale type used to scale the preview.
Returns:
The current CameraView.CaptureMode.
Sets the CameraView capture mode
This controls only image or video capture function is enabled or both are enabled.
Parameters:
captureMode: The desired CameraView.CaptureMode.
public long
getMaxVideoDuration()
Returns the maximum duration of videos, or CameraView.INDEFINITE_VIDEO_DURATION if there is no
timeout.
Takes a picture, and calls ImageCapture.OnImageCapturedCallback.onCaptureSuccess(ImageProxy)
once when done.
Parameters:
executor: The executor in which the callback methods will be run.
callback: Callback which will receive success or failure callbacks.
Takes a picture and calls
ImageCapture.OnImageSavedCallback.onImageSaved(ImageCapture.OutputFileResults) when done.
The value of in the
will be overwritten based on camera direction. For
front camera, it will be set to true; for back camera, it will be set to false.
Parameters:
outputFileOptions: Options to store the newly captured image.
executor: The executor in which the callback methods will be run.
callback: Callback which will receive success or failure.
public void
startRecording(java.io.File file, java.util.concurrent.Executor executor,
OnVideoSavedCallback callback)
Takes a video and calls the OnVideoSavedCallback when done.
Parameters:
file: The destination.
executor: The executor in which the callback methods will be run.
callback: Callback which will receive success or failure.
public void
startRecording(ParcelFileDescriptor fd, java.util.concurrent.Executor executor,
OnVideoSavedCallback callback)
Takes a video and calls the OnVideoSavedCallback when done.
Parameters:
fd: The destination .
executor: The executor in which the callback methods will be run.
callback: Callback which will receive success or failure.
public void
stopRecording()
Stops an in progress video.
public boolean
isRecording()
Returns:
True if currently recording.
public boolean
hasCameraWithLensFacing(int lensFacing)
Queries whether the current device has a camera with the specified direction.
Returns:
True if the device supports the direction.
public void
toggleCamera()
Toggles between the primary front facing camera and the primary back facing camera.
This will have no effect if not already bound to a lifecycle via CameraView.bindToLifecycle(LifecycleOwner).
public void
setCameraLensFacing(java.lang.Integer lensFacing)
Sets the desired camera by specifying desired lensFacing.
This will choose the primary camera with the specified camera lensFacing.
If called before CameraView.bindToLifecycle(LifecycleOwner), this will set the camera to be
used when first bound to the lifecycle. If the specified lensFacing is not supported by the
device, as determined by CameraView.hasCameraWithLensFacing(int), the first supported
lensFacing will be chosen when CameraView.bindToLifecycle(LifecycleOwner) is called.
If called with null AFTER binding to the lifecycle, the behavior would be
equivalent to unbind the use cases without the lifecycle having to be destroyed.
Parameters:
lensFacing: The desired camera lensFacing.
public java.lang.Integer
getCameraLensFacing()
Returns the currently selected lensFacing.
Gets the active flash strategy.
public void
setFlash(int flashMode)
Sets the active flash strategy.
public boolean
onTouchEvent(MotionEvent event)
public boolean
performClick()
Focus the position of the touch event, or focus the center of the preview for
accessibility events
public boolean
isPinchToZoomEnabled()
Returns whether the view allows pinch-to-zoom.
Returns:
True if pinch to zoom is enabled.
public void
setPinchToZoomEnabled(boolean enabled)
Sets whether the view should allow pinch-to-zoom.
When enabled, the user can pinch the camera to zoom in/out. This only has an effect if the
bound camera supports zoom.
Parameters:
enabled: True to enable pinch-to-zoom.
public float
getZoomRatio()
Returns the current zoom ratio.
Returns:
The current zoom ratio.
public void
setZoomRatio(float zoomRatio)
Sets the current zoom ratio.
Valid zoom values range from CameraView.getMinZoomRatio() to CameraView.getMaxZoomRatio().
Parameters:
zoomRatio: The requested zoom ratio.
public float
getMinZoomRatio()
Returns the minimum zoom ratio.
For most cameras this should return a zoom ratio of 1. A zoom ratio of 1 corresponds to a
non-zoomed image.
Returns:
The minimum zoom ratio.
public float
getMaxZoomRatio()
Returns the maximum zoom ratio.
The zoom ratio corresponds to the ratio between both the widths and heights of a
non-zoomed image and a maximally zoomed image for the selected camera.
Returns:
The maximum zoom ratio.
public boolean
isZoomSupported()
Returns whether the bound camera supports zooming.
Returns:
True if the camera supports zooming.
public void
enableTorch(boolean torch)
Turns on/off torch.
Parameters:
torch: True to turn on torch, false to turn off torch.
public boolean
isTorchOn()
Returns current torch status.
Returns:
true if torch is on , otherwise false
Source
/*
* Copyright (C) 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.view;
import android.Manifest.permission;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Display;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.Surface;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresPermission;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.FocusMeteringResult;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCapture.OnImageCapturedCallback;
import androidx.camera.core.ImageCapture.OnImageSavedCallback;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Logger;
import androidx.camera.core.MeteringPoint;
import androidx.camera.core.MeteringPointFactory;
import androidx.camera.core.VideoCapture;
import androidx.camera.core.impl.LensFacingConverter;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.impl.utils.futures.FutureCallback;
import androidx.camera.core.impl.utils.futures.Futures;
import androidx.camera.view.video.ExperimentalVideo;
import androidx.camera.view.video.OnVideoSavedCallback;
import androidx.camera.view.video.OutputFileOptions;
import androidx.camera.view.video.OutputFileResults;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.util.concurrent.Executor;
/**
* A {@link View} that displays a preview of the camera with methods {@link
* #takePicture(Executor, OnImageCapturedCallback)},
* {@link #takePicture(ImageCapture.OutputFileOptions, Executor, OnImageSavedCallback)},
* {@link #startRecording(File, Executor, OnVideoSavedCallback callback)}
* and {@link #stopRecording()}.
*
* <p>Because the Camera is a limited resource and consumes a high amount of power, CameraView must
* be opened/closed. CameraView will handle opening/closing automatically through use of a {@link
* LifecycleOwner}. Use {@link #bindToLifecycle(LifecycleOwner)} to start the camera.
*/
public final class CameraView extends FrameLayout {
static final String TAG = CameraView.class.getSimpleName();
static final int INDEFINITE_VIDEO_DURATION = -1;
static final int INDEFINITE_VIDEO_SIZE = -1;
private static final String EXTRA_SUPER = "super";
private static final String EXTRA_ZOOM_RATIO = "zoom_ratio";
private static final String EXTRA_PINCH_TO_ZOOM_ENABLED = "pinch_to_zoom_enabled";
private static final String EXTRA_FLASH = "flash";
private static final String EXTRA_MAX_VIDEO_DURATION = "max_video_duration";
private static final String EXTRA_MAX_VIDEO_SIZE = "max_video_size";
private static final String EXTRA_SCALE_TYPE = "scale_type";
private static final String EXTRA_CAMERA_DIRECTION = "camera_direction";
private static final String EXTRA_CAPTURE_MODE = "captureMode";
private static final int LENS_FACING_NONE = 0;
private static final int LENS_FACING_FRONT = 1;
private static final int LENS_FACING_BACK = 2;
private static final int FLASH_MODE_AUTO = 1;
private static final int FLASH_MODE_ON = 2;
private static final int FLASH_MODE_OFF = 4;
// For tap-to-focus
private long mDownEventTimestamp;
// For pinch-to-zoom
private PinchToZoomGestureDetector mPinchToZoomGestureDetector;
private boolean mIsPinchToZoomEnabled = true;
CameraXModule mCameraModule;
private final DisplayManager.DisplayListener mDisplayListener =
new DisplayListener() {
@Override
public void onDisplayAdded(int displayId) {
}
@Override
public void onDisplayRemoved(int displayId) {
}
@Override
public void onDisplayChanged(int displayId) {
mCameraModule.invalidateView();
}
};
private PreviewView mPreviewView;
// For accessibility event
private MotionEvent mUpEvent;
public CameraView(@NonNull Context context) {
this(context, null);
}
public CameraView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CameraView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
@RequiresApi(21)
public CameraView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
/**
* Binds control of the camera used by this view to the given lifecycle.
*
* <p>This links opening/closing the camera to the given lifecycle. The camera will not operate
* unless this method is called with a valid {@link LifecycleOwner} that is not in the {@link
* androidx.lifecycle.Lifecycle.State#DESTROYED} state. Call this method only once camera
* permissions have been obtained.
*
* <p>Once the provided lifecycle has transitioned to a {@link
* androidx.lifecycle.Lifecycle.State#DESTROYED} state, CameraView must be bound to a new
* lifecycle through this method in order to operate the camera.
*
* @param lifecycleOwner The lifecycle that will control this view's camera
* @throws IllegalArgumentException if provided lifecycle is in a {@link
* androidx.lifecycle.Lifecycle.State#DESTROYED} state.
* @throws IllegalStateException if camera permissions are not granted.
*/
@RequiresPermission(permission.CAMERA)
public void bindToLifecycle(@NonNull LifecycleOwner lifecycleOwner) {
mCameraModule.bindToLifecycle(lifecycleOwner);
}
private void init(Context context, @Nullable AttributeSet attrs) {
addView(mPreviewView = new PreviewView(getContext()), 0 /* view position */);
mCameraModule = new CameraXModule(this);
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CameraView);
setScaleType(
PreviewView.ScaleType.fromId(
a.getInteger(R.styleable.CameraView_scaleType,
getScaleType().getId())));
setPinchToZoomEnabled(
a.getBoolean(
R.styleable.CameraView_pinchToZoomEnabled, isPinchToZoomEnabled()));
setCaptureMode(
CaptureMode.fromId(
a.getInteger(R.styleable.CameraView_captureMode,
getCaptureMode().getId())));
int lensFacing = a.getInt(R.styleable.CameraView_lensFacing, LENS_FACING_BACK);
switch (lensFacing) {
case LENS_FACING_NONE:
setCameraLensFacing(null);
break;
case LENS_FACING_FRONT:
setCameraLensFacing(CameraSelector.LENS_FACING_FRONT);
break;
case LENS_FACING_BACK:
setCameraLensFacing(CameraSelector.LENS_FACING_BACK);
break;
default:
// Unhandled event.
}
int flashMode = a.getInt(R.styleable.CameraView_flash, 0);
switch (flashMode) {
case FLASH_MODE_AUTO:
setFlash(ImageCapture.FLASH_MODE_AUTO);
break;
case FLASH_MODE_ON:
setFlash(ImageCapture.FLASH_MODE_ON);
break;
case FLASH_MODE_OFF:
setFlash(ImageCapture.FLASH_MODE_OFF);
break;
default:
// Unhandled event.
}
a.recycle();
}
if (getBackground() == null) {
setBackgroundColor(0xFF111111);
}
mPinchToZoomGestureDetector = new PinchToZoomGestureDetector(context);
}
@Override
@NonNull
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}
@Override
@NonNull
protected Parcelable onSaveInstanceState() {
// TODO(b/113884082): Decide what belongs here or what should be invalidated on
// configuration
// change
Bundle state = new Bundle();
state.putParcelable(EXTRA_SUPER, super.onSaveInstanceState());
state.putInt(EXTRA_SCALE_TYPE, getScaleType().getId());
state.putFloat(EXTRA_ZOOM_RATIO, getZoomRatio());
state.putBoolean(EXTRA_PINCH_TO_ZOOM_ENABLED, isPinchToZoomEnabled());
state.putString(EXTRA_FLASH, FlashModeConverter.nameOf(getFlash()));
state.putLong(EXTRA_MAX_VIDEO_DURATION, getMaxVideoDuration());
state.putLong(EXTRA_MAX_VIDEO_SIZE, getMaxVideoSize());
if (getCameraLensFacing() != null) {
state.putString(EXTRA_CAMERA_DIRECTION,
LensFacingConverter.nameOf(getCameraLensFacing()));
}
state.putInt(EXTRA_CAPTURE_MODE, getCaptureMode().getId());
return state;
}
@Override
protected void onRestoreInstanceState(@Nullable Parcelable savedState) {
// TODO(b/113884082): Decide what belongs here or what should be invalidated on
// configuration
// change
if (savedState instanceof Bundle) {
Bundle state = (Bundle) savedState;
super.onRestoreInstanceState(state.getParcelable(EXTRA_SUPER));
setScaleType(PreviewView.ScaleType.fromId(state.getInt(EXTRA_SCALE_TYPE)));
setZoomRatio(state.getFloat(EXTRA_ZOOM_RATIO));
setPinchToZoomEnabled(state.getBoolean(EXTRA_PINCH_TO_ZOOM_ENABLED));
setFlash(FlashModeConverter.valueOf(state.getString(EXTRA_FLASH)));
setMaxVideoDuration(state.getLong(EXTRA_MAX_VIDEO_DURATION));
setMaxVideoSize(state.getLong(EXTRA_MAX_VIDEO_SIZE));
String lensFacingString = state.getString(EXTRA_CAMERA_DIRECTION);
setCameraLensFacing(
TextUtils.isEmpty(lensFacingString)
? null
: LensFacingConverter.valueOf(lensFacingString));
setCaptureMode(CaptureMode.fromId(state.getInt(EXTRA_CAPTURE_MODE)));
} else {
super.onRestoreInstanceState(savedState);
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
DisplayManager dpyMgr =
(DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
dpyMgr.registerDisplayListener(mDisplayListener, new Handler(Looper.getMainLooper()));
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
DisplayManager dpyMgr =
(DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
dpyMgr.unregisterDisplayListener(mDisplayListener);
}
/**
* Gets the {@link LiveData} of the underlying {@link PreviewView}'s
* {@link PreviewView.StreamState}.
*
* @return A {@link LiveData} containing the {@link PreviewView.StreamState}. Apps can either
* get current value by {@link LiveData#getValue()} or register a observer by
* {@link LiveData#observe}.
* @see PreviewView#getPreviewStreamState()
*/
@NonNull
public LiveData<PreviewView.StreamState> getPreviewStreamState() {
return mPreviewView.getPreviewStreamState();
}
@NonNull
PreviewView getPreviewView() {
return mPreviewView;
}
// TODO(b/124269166): Rethink how we can handle permissions here.
@SuppressLint("MissingPermission")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Since bindToLifecycle will depend on the measured dimension, only call it when measured
// dimension is not 0x0
if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {
mCameraModule.bindToLifecycleAfterViewMeasured();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
// TODO(b/124269166): Rethink how we can handle permissions here.
@SuppressLint("MissingPermission")
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// In case that the CameraView size is always set as 0x0, we still need to trigger to force
// binding to lifecycle
mCameraModule.bindToLifecycleAfterViewMeasured();
mCameraModule.invalidateView();
super.onLayout(changed, left, top, right, bottom);
}
/**
* @return One of {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90}, {@link
* Surface#ROTATION_180}, {@link Surface#ROTATION_270}.
*/
int getDisplaySurfaceRotation() {
Display display = getDisplay();
// Null when the View is detached. If we were in the middle of a background operation,
// better to not NPE. When the background operation finishes, it'll realize that the camera
// was closed.
if (display == null) {
return 0;
}
return display.getRotation();
}
/**
* Returns the scale type used to scale the preview.
*
* @return The current {@link PreviewView.ScaleType}.
*/
@NonNull
public PreviewView.ScaleType getScaleType() {
return mPreviewView.getScaleType();
}
/**
* Sets the view finder scale type.
*
* <p>This controls how the view finder should be scaled and positioned within the view.
*
* @param scaleType The desired {@link PreviewView.ScaleType}.
*/
public void setScaleType(@NonNull PreviewView.ScaleType scaleType) {
mPreviewView.setScaleType(scaleType);
}
/**
* Returns the scale type used to scale the preview.
*
* @return The current {@link CaptureMode}.
*/
@NonNull
public CaptureMode getCaptureMode() {
return mCameraModule.getCaptureMode();
}
/**
* Sets the CameraView capture mode
*
* <p>This controls only image or video capture function is enabled or both are enabled.
*
* @param captureMode The desired {@link CaptureMode}.
*/
public void setCaptureMode(@NonNull CaptureMode captureMode) {
mCameraModule.setCaptureMode(captureMode);
}
/**
* Returns the maximum duration of videos, or {@link #INDEFINITE_VIDEO_DURATION} if there is no
* timeout.
*
* @hide Not currently implemented.
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public long getMaxVideoDuration() {
return mCameraModule.getMaxVideoDuration();
}
/**
* Sets the maximum video duration before
* {@link OnVideoSavedCallback#onVideoSaved(OutputFileResults)} is called
* automatically.
* Use {@link #INDEFINITE_VIDEO_DURATION} to disable the timeout.
*/
private void setMaxVideoDuration(long duration) {
mCameraModule.setMaxVideoDuration(duration);
}
/**
* Returns the maximum size of videos in bytes, or {@link #INDEFINITE_VIDEO_SIZE} if there is no
* timeout.
*/
private long getMaxVideoSize() {
return mCameraModule.getMaxVideoSize();
}
/**
* Sets the maximum video size in bytes before
* {@link OnVideoSavedCallback#onVideoSaved(OutputFileResults)}
* is called automatically. Use {@link #INDEFINITE_VIDEO_SIZE} to disable the size restriction.
*/
private void setMaxVideoSize(long size) {
mCameraModule.setMaxVideoSize(size);
}
/**
* Takes a picture, and calls {@link OnImageCapturedCallback#onCaptureSuccess(ImageProxy)}
* once when done.
*
* @param executor The executor in which the callback methods will be run.
* @param callback Callback which will receive success or failure callbacks.
*/
public void takePicture(@NonNull Executor executor, @NonNull OnImageCapturedCallback callback) {
mCameraModule.takePicture(executor, callback);
}
/**
* Takes a picture and calls
* {@link OnImageSavedCallback#onImageSaved(ImageCapture.OutputFileResults)} when done.
*
* <p> The value of {@link ImageCapture.Metadata#isReversedHorizontal()} in the
* {@link ImageCapture.OutputFileOptions} will be overwritten based on camera direction. For
* front camera, it will be set to true; for back camera, it will be set to false.
*
* @param outputFileOptions Options to store the newly captured image.
* @param executor The executor in which the callback methods will be run.
* @param callback Callback which will receive success or failure.
*/
public void takePicture(@NonNull ImageCapture.OutputFileOptions outputFileOptions,
@NonNull Executor executor,
@NonNull OnImageSavedCallback callback) {
mCameraModule.takePicture(outputFileOptions, executor, callback);
}
/**
* Takes a video and calls the OnVideoSavedCallback when done.
*
* @param file The destination.
* @param executor The executor in which the callback methods will be run.
* @param callback Callback which will receive success or failure.
*/
@ExperimentalVideo
public void startRecording(@NonNull File file, @NonNull Executor executor,
@NonNull OnVideoSavedCallback callback) {
OutputFileOptions options = OutputFileOptions.builder(file).build();
startRecording(options, executor, callback);
}
/**
* Takes a video and calls the OnVideoSavedCallback when done.
*
* @param fd The destination {@link ParcelFileDescriptor}.
* @param executor The executor in which the callback methods will be run.
* @param callback Callback which will receive success or failure.
*/
@ExperimentalVideo
public void startRecording(@NonNull ParcelFileDescriptor fd, @NonNull Executor executor,
@NonNull OnVideoSavedCallback callback) {
OutputFileOptions options = OutputFileOptions.builder(fd).build();
startRecording(options, executor, callback);
}
/**
* Takes a video and calls the OnVideoSavedCallback when done.
*
* @param outputFileOptions Options to store the newly captured video.
* @param executor The executor in which the callback methods will be run.
* @param callback Callback which will receive success or failure.
*/
@ExperimentalVideo
public void startRecording(@NonNull OutputFileOptions outputFileOptions,
@NonNull Executor executor,
@NonNull OnVideoSavedCallback callback) {
VideoCapture.OnVideoSavedCallback callbackWrapper =
new VideoCapture.OnVideoSavedCallback() {
@Override
public void onVideoSaved(
@NonNull VideoCapture.OutputFileResults outputFileResults) {
callback.onVideoSaved(
OutputFileResults.create(outputFileResults.getSavedUri()));
}
@Override
public void onError(int videoCaptureError, @NonNull String message,
@Nullable Throwable cause) {
callback.onError(videoCaptureError, message, cause);
}
};
mCameraModule.startRecording(outputFileOptions.toVideoCaptureOutputFileOptions(), executor,
callbackWrapper);
}
/** Stops an in progress video. */
@ExperimentalVideo
public void stopRecording() {
mCameraModule.stopRecording();
}
/** @return True if currently recording. */
@ExperimentalVideo
public boolean isRecording() {
return mCameraModule.isRecording();
}
/**
* Queries whether the current device has a camera with the specified direction.
*
* @return True if the device supports the direction.
* @throws IllegalStateException if the CAMERA permission is not currently granted.
*/
@RequiresPermission(permission.CAMERA)
public boolean hasCameraWithLensFacing(@CameraSelector.LensFacing int lensFacing) {
return mCameraModule.hasCameraWithLensFacing(lensFacing);
}
/**
* Toggles between the primary front facing camera and the primary back facing camera.
*
* <p>This will have no effect if not already bound to a lifecycle via {@link
* #bindToLifecycle(LifecycleOwner)}.
*/
public void toggleCamera() {
mCameraModule.toggleCamera();
}
/**
* Sets the desired camera by specifying desired lensFacing.
*
* <p>This will choose the primary camera with the specified camera lensFacing.
*
* <p>If called before {@link #bindToLifecycle(LifecycleOwner)}, this will set the camera to be
* used when first bound to the lifecycle. If the specified lensFacing is not supported by the
* device, as determined by {@link #hasCameraWithLensFacing(int)}, the first supported
* lensFacing will be chosen when {@link #bindToLifecycle(LifecycleOwner)} is called.
*
* <p>If called with {@code null} AFTER binding to the lifecycle, the behavior would be
* equivalent to unbind the use cases without the lifecycle having to be destroyed.
*
* @param lensFacing The desired camera lensFacing.
*/
public void setCameraLensFacing(@Nullable Integer lensFacing) {
mCameraModule.setCameraLensFacing(lensFacing);
}
/** Returns the currently selected lensFacing. */
@Nullable
public Integer getCameraLensFacing() {
return mCameraModule.getLensFacing();
}
/** Gets the active flash strategy. */
@ImageCapture.FlashMode
public int getFlash() {
return mCameraModule.getFlash();
}
/** Sets the active flash strategy. */
public void setFlash(@ImageCapture.FlashMode int flashMode) {
mCameraModule.setFlash(flashMode);
}
private long delta() {
return System.currentTimeMillis() - mDownEventTimestamp;
}
@Override
public boolean onTouchEvent(@NonNull MotionEvent event) {
// Disable pinch-to-zoom and tap-to-focus while the camera module is paused.
if (mCameraModule.isPaused()) {
return false;
}
// Only forward the event to the pinch-to-zoom gesture detector when pinch-to-zoom is
// enabled.
if (isPinchToZoomEnabled()) {
mPinchToZoomGestureDetector.onTouchEvent(event);
}
if (event.getPointerCount() == 2 && isPinchToZoomEnabled() && isZoomSupported()) {
return true;
}
// Camera focus
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownEventTimestamp = System.currentTimeMillis();
break;
case MotionEvent.ACTION_UP:
if (delta() < ViewConfiguration.getLongPressTimeout()
&& mCameraModule.isBoundToLifecycle()) {
mUpEvent = event;
performClick();
}
break;
default:
// Unhandled event.
return false;
}
return true;
}
/**
* Focus the position of the touch event, or focus the center of the preview for
* accessibility events
*/
@Override
public boolean performClick() {
super.performClick();
final float x = (mUpEvent != null) ? mUpEvent.getX() : getX() + getWidth() / 2f;
final float y = (mUpEvent != null) ? mUpEvent.getY() : getY() + getHeight() / 2f;
mUpEvent = null;
Camera camera = mCameraModule.getCamera();
if (camera != null) {
MeteringPointFactory pointFactory = mPreviewView.getMeteringPointFactory();
float afPointWidth = 1.0f / 6.0f; // 1/6 total area
float aePointWidth = afPointWidth * 1.5f;
MeteringPoint afPoint = pointFactory.createPoint(x, y, afPointWidth);
MeteringPoint aePoint = pointFactory.createPoint(x, y, aePointWidth);
ListenableFuture<FocusMeteringResult> future =
camera.getCameraControl().startFocusAndMetering(
new FocusMeteringAction.Builder(afPoint,
FocusMeteringAction.FLAG_AF).addPoint(aePoint,
FocusMeteringAction.FLAG_AE).build());
Futures.addCallback(future, new FutureCallback<FocusMeteringResult>() {
@Override
public void onSuccess(@Nullable FocusMeteringResult result) {
}
@Override
public void onFailure(Throwable t) {
// Throw the unexpected error.
throw new RuntimeException(t);
}
}, CameraXExecutors.directExecutor());
} else {
Logger.d(TAG, "cannot access camera");
}
return true;
}
float rangeLimit(float val, float max, float min) {
return Math.min(Math.max(val, min), max);
}
/**
* Returns whether the view allows pinch-to-zoom.
*
* @return True if pinch to zoom is enabled.
*/
public boolean isPinchToZoomEnabled() {
return mIsPinchToZoomEnabled;
}
/**
* Sets whether the view should allow pinch-to-zoom.
*
* <p>When enabled, the user can pinch the camera to zoom in/out. This only has an effect if the
* bound camera supports zoom.
*
* @param enabled True to enable pinch-to-zoom.
*/
public void setPinchToZoomEnabled(boolean enabled) {
mIsPinchToZoomEnabled = enabled;
}
/**
* Returns the current zoom ratio.
*
* @return The current zoom ratio.
*/
public float getZoomRatio() {
return mCameraModule.getZoomRatio();
}
/**
* Sets the current zoom ratio.
*
* <p>Valid zoom values range from {@link #getMinZoomRatio()} to {@link #getMaxZoomRatio()}.
*
* @param zoomRatio The requested zoom ratio.
*/
public void setZoomRatio(float zoomRatio) {
mCameraModule.setZoomRatio(zoomRatio);
}
/**
* Returns the minimum zoom ratio.
*
* <p>For most cameras this should return a zoom ratio of 1. A zoom ratio of 1 corresponds to a
* non-zoomed image.
*
* @return The minimum zoom ratio.
*/
public float getMinZoomRatio() {
return mCameraModule.getMinZoomRatio();
}
/**
* Returns the maximum zoom ratio.
*
* <p>The zoom ratio corresponds to the ratio between both the widths and heights of a
* non-zoomed image and a maximally zoomed image for the selected camera.
*
* @return The maximum zoom ratio.
*/
public float getMaxZoomRatio() {
return mCameraModule.getMaxZoomRatio();
}
/**
* Returns whether the bound camera supports zooming.
*
* @return True if the camera supports zooming.
*/
public boolean isZoomSupported() {
return mCameraModule.isZoomSupported();
}
/**
* Turns on/off torch.
*
* @param torch True to turn on torch, false to turn off torch.
*/
public void enableTorch(boolean torch) {
mCameraModule.enableTorch(torch);
}
/**
* Returns current torch status.
*
* @return true if torch is on , otherwise false
*/
public boolean isTorchOn() {
return mCameraModule.isTorchOn();
}
/**
* The capture mode used by CameraView.
*
* <p>This enum can be used to determine which capture mode will be enabled for {@link
* CameraView}.
*/
public enum CaptureMode {
/** A mode where image capture is enabled. */
IMAGE(0),
/** A mode where video capture is enabled. */
@ExperimentalVideo
VIDEO(1),
/**
* A mode where both image capture and video capture are simultaneously enabled. Note that
* this mode may not be available on every device.
*/
@ExperimentalVideo
MIXED(2);
private final int mId;
int getId() {
return mId;
}
CaptureMode(int id) {
mId = id;
}
static CaptureMode fromId(int id) {
for (CaptureMode f : values()) {
if (f.mId == id) {
return f;
}
}
throw new IllegalArgumentException();
}
}
static class S extends ScaleGestureDetector.SimpleOnScaleGestureListener {
private ScaleGestureDetector.OnScaleGestureListener mListener;
void setRealGestureDetector(ScaleGestureDetector.OnScaleGestureListener l) {
mListener = l;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
return mListener.onScale(detector);
}
}
private class PinchToZoomGestureDetector extends ScaleGestureDetector
implements ScaleGestureDetector.OnScaleGestureListener {
PinchToZoomGestureDetector(Context context) {
this(context, new S());
}
PinchToZoomGestureDetector(Context context, S s) {
super(context, s);
s.setRealGestureDetector(this);
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scale = detector.getScaleFactor();
// Speeding up the zoom by 2X.
if (scale > 1f) {
scale = 1.0f + (scale - 1.0f) * 2;
} else {
scale = 1.0f - (1.0f - scale) * 2;
}
float newRatio = getZoomRatio() * scale;
newRatio = rangeLimit(newRatio, getMaxZoomRatio(), getMinZoomRatio());
setZoomRatio(newRatio);
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
}
}