Smart sizing behavior, removed adjustViewBounds

pull/1/head
Mattia Iavarone 7 years ago
parent 3dbd7fa945
commit e7330fcd7b
  1. 14
      camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java
  2. 1
      camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java
  3. 52
      camerakit/src/main/base/com/flurgle/camerakit/PreviewImpl.java
  4. 1
      camerakit/src/main/base/com/flurgle/camerakit/SurfaceViewPreview.java
  5. 1
      camerakit/src/main/base/com/flurgle/camerakit/TextureViewPreview.java
  6. 191
      camerakit/src/main/java/com/flurgle/camerakit/CameraView.java
  7. 10
      demo/src/main/java/com/flurgle/camerakit/demo/MainActivity.java
  8. 13
      demo/src/main/java/com/flurgle/camerakit/demo/PicturePreviewActivity.java
  9. 50
      demo/src/main/java/com/flurgle/camerakit/demo/ResultHolder.java
  10. 7
      demo/src/main/java/com/flurgle/camerakit/demo/VideoPreviewActivity.java

@ -43,7 +43,6 @@ class Camera1 extends CameraImpl {
private MediaRecorder mMediaRecorder; private MediaRecorder mMediaRecorder;
private File mVideoFile; private File mVideoFile;
private Camera.AutoFocusCallback mAutofocusCallback; private Camera.AutoFocusCallback mAutofocusCallback;
private boolean isCapturingImage = false;
private int mDisplayOffset; private int mDisplayOffset;
private int mDeviceOrientation; private int mDeviceOrientation;
@ -63,6 +62,7 @@ class Camera1 extends CameraImpl {
private Handler mFocusHandler = new Handler(); private Handler mFocusHandler = new Handler();
private ConstantMapper.MapperImpl mMapper = new ConstantMapper.Mapper1(); private ConstantMapper.MapperImpl mMapper = new ConstantMapper.Mapper1();
private boolean mIsSetup = false; private boolean mIsSetup = false;
private boolean mIsCapturingImage = false;
private final Object mLock = new Object(); private final Object mLock = new Object();
@ -91,6 +91,7 @@ class Camera1 extends CameraImpl {
Size newSize = computePreviewSize(); Size newSize = computePreviewSize();
if (!newSize.equals(mPreviewSize)) { if (!newSize.equals(mPreviewSize)) {
mPreviewSize = newSize; mPreviewSize = newSize;
mCameraListener.onCameraPreviewSizeChanged();
synchronized (mLock) { synchronized (mLock) {
mCamera.stopPreview(); mCamera.stopPreview();
Camera.Parameters params = mCamera.getParameters(); Camera.Parameters params = mCamera.getParameters();
@ -127,6 +128,7 @@ class Camera1 extends CameraImpl {
boolean invertPreviewSizes = shouldFlipSizes(); // mDisplayOffset % 180 != 0; boolean invertPreviewSizes = shouldFlipSizes(); // mDisplayOffset % 180 != 0;
mCaptureSize = computeCaptureSize(); mCaptureSize = computeCaptureSize();
mPreviewSize = computePreviewSize(); mPreviewSize = computePreviewSize();
mCameraListener.onCameraPreviewSizeChanged();
mPreview.setDesiredSize( mPreview.setDesiredSize(
invertPreviewSizes ? mPreviewSize.getHeight() : mPreviewSize.getWidth(), invertPreviewSizes ? mPreviewSize.getHeight() : mPreviewSize.getWidth(),
invertPreviewSizes ? mPreviewSize.getWidth() : mPreviewSize.getHeight() invertPreviewSizes ? mPreviewSize.getWidth() : mPreviewSize.getHeight()
@ -356,12 +358,12 @@ class Camera1 extends CameraImpl {
@Override @Override
void captureImage() { void captureImage() {
if (isCapturingImage) return; if (mIsCapturingImage) return;
if (!isCameraOpened()) return; if (!isCameraOpened()) return;
switch (mSessionType) { switch (mSessionType) {
case SESSION_TYPE_PICTURE: case SESSION_TYPE_PICTURE:
// Set boolean to wait for image callback // Set boolean to wait for image callback
isCapturingImage = true; mIsCapturingImage = true;
synchronized (mLock) { synchronized (mLock) {
Camera.Parameters parameters = mCamera.getParameters(); Camera.Parameters parameters = mCamera.getParameters();
parameters.setRotation(computeExifOrientation()); parameters.setRotation(computeExifOrientation());
@ -372,7 +374,7 @@ class Camera1 extends CameraImpl {
@Override @Override
public void onPictureTaken(byte[] data, Camera camera) { public void onPictureTaken(byte[] data, Camera camera) {
mCameraListener.onPictureTaken(data); mCameraListener.onPictureTaken(data);
isCapturingImage = false; mIsCapturingImage = false;
camera.startPreview(); // This is needed, read somewhere in the docs. camera.startPreview(); // This is needed, read somewhere in the docs.
} }
}); });
@ -382,7 +384,7 @@ class Camera1 extends CameraImpl {
// If we are in a video session, camera captures are fast captures coming // If we are in a video session, camera captures are fast captures coming
// from the preview stream. // from the preview stream.
// TODO: will this work while recording a video? test... // TODO: will this work while recording a video? test...
isCapturingImage = true; mIsCapturingImage = true;
mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() { mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
@Override @Override
public void onPreviewFrame(final byte[] data, Camera camera) { public void onPreviewFrame(final byte[] data, Camera camera) {
@ -402,7 +404,7 @@ class Camera1 extends CameraImpl {
byte[] rotatedData = RotationHelper.rotate(data, preWidth, preHeight, rotation); byte[] rotatedData = RotationHelper.rotate(data, preWidth, preHeight, rotation);
YuvImage yuv = new YuvImage(rotatedData, format, postWidth, postHeight, null); YuvImage yuv = new YuvImage(rotatedData, format, postWidth, postHeight, null);
mCameraListener.processYuvImage(yuv); mCameraListener.processYuvImage(yuv);
isCapturingImage = false; mIsCapturingImage = false;
} }
}).start(); }).start();
} }

@ -1,5 +1,6 @@
package com.flurgle.camerakit; package com.flurgle.camerakit;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.view.MotionEvent; import android.view.MotionEvent;

@ -1,12 +1,16 @@
package com.flurgle.camerakit; package com.flurgle.camerakit;
import android.content.Context;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.view.Surface; import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
abstract class PreviewImpl { abstract class PreviewImpl {
// This is used to notify CameraImpl to recompute its camera Preview size.
// After that, CameraView will need a new layout pass to adapt to the Preview size.
interface SurfaceCallback { interface SurfaceCallback {
void onSurfaceAvailable(); void onSurfaceAvailable();
void onSurfaceChanged(); void onSurfaceChanged();
@ -23,35 +27,48 @@ abstract class PreviewImpl {
private int mDesiredWidth; private int mDesiredWidth;
private int mDesiredHeight; private int mDesiredHeight;
void setSurfaceCallback(SurfaceCallback callback) { PreviewImpl(Context context, ViewGroup parent) {}
mSurfaceCallback = callback;
}
abstract Surface getSurface(); abstract Surface getSurface();
abstract View getView(); abstract View getView();
abstract Class getOutputClass(); abstract Class getOutputClass();
abstract boolean isReady();
protected void onDisplayOffset(int displayOrientation) {} protected void onDisplayOffset(int displayOrientation) {}
protected void onDeviceOrientation(int deviceOrientation) {} protected void onDeviceOrientation(int deviceOrientation) {}
abstract boolean isReady();
SurfaceHolder getSurfaceHolder() { SurfaceHolder getSurfaceHolder() {
return null; return null;
} }
SurfaceTexture getSurfaceTexture() { SurfaceTexture getSurfaceTexture() {
return null; return null;
} }
// As far as I can see, these are the actual preview dimensions, as set in CameraParameters.
// This is called by the CameraImpl.
// These must be alredy rotated, if needed, to be consistent with surface/view sizes.
void setDesiredSize(int width, int height) {
this.mDesiredWidth = width;
this.mDesiredHeight = height;
refreshScale();
}
final Size getSurfaceSize() {
return new Size(mSurfaceWidth, mSurfaceHeight);
}
final void setSurfaceCallback(SurfaceCallback callback) {
mSurfaceCallback = callback;
}
protected final void onSurfaceAvailable(int width, int height) { protected final void onSurfaceAvailable(int width, int height) {
mSurfaceWidth = width; mSurfaceWidth = width;
mSurfaceHeight = height; mSurfaceHeight = height;
refreshScale(); refreshScale();
mSurfaceCallback.onSurfaceAvailable(); mSurfaceCallback.onSurfaceAvailable();
} }
// As far as I can see, these are the view/surface dimensions. // As far as I can see, these are the view/surface dimensions.
// This is called by subclasses. // This is called by subclasses.
protected final void onSurfaceSizeChanged(int width, int height) { protected final void onSurfaceSizeChanged(int width, int height) {
@ -63,31 +80,20 @@ abstract class PreviewImpl {
} }
} }
protected final void onSurfaceDestroyed() { protected final void onSurfaceDestroyed() {
mSurfaceWidth = 0; mSurfaceWidth = 0;
mSurfaceHeight = 0; mSurfaceHeight = 0;
refreshScale(); refreshScale();
} }
// As far as I can see, these are the actual preview dimensions, as set in CameraParameters.
// This is called by the CameraImpl.
// These must be alredy rotated, if needed, to be consistent with surface/view sizes.
void setDesiredSize(int width, int height) {
this.mDesiredWidth = width;
this.mDesiredHeight = height;
refreshScale();
}
Size getSurfaceSize() {
return new Size(mSurfaceWidth, mSurfaceHeight);
}
/** /**
* As far as I can see, this extends either width or height of the surface, * As far as I can see, this extends either width or height of the surface,
* to match the desired aspect ratio. * to match the desired aspect ratio.
* This means that the external part of the surface will be cropped by the outer view. * This means that the external part of the surface will be cropped by the outer view.
*/ */
private void refreshScale() { private final void refreshScale() {
getView().post(new Runnable() { getView().post(new Runnable() {
@Override @Override
public void run() { public void run() {

@ -13,6 +13,7 @@ class SurfaceViewPreview extends PreviewImpl {
private final SurfaceView mSurfaceView; private final SurfaceView mSurfaceView;
SurfaceViewPreview(Context context, ViewGroup parent) { SurfaceViewPreview(Context context, ViewGroup parent) {
super(context, parent);
final View view = View.inflate(context, R.layout.surface_view, parent); // MATCH_PARENT final View view = View.inflate(context, R.layout.surface_view, parent); // MATCH_PARENT
mSurfaceView = (SurfaceView) view.findViewById(R.id.surface_view); mSurfaceView = (SurfaceView) view.findViewById(R.id.surface_view);
final SurfaceHolder holder = mSurfaceView.getHolder(); final SurfaceHolder holder = mSurfaceView.getHolder();

@ -15,6 +15,7 @@ class TextureViewPreview extends PreviewImpl {
private Surface mSurface; private Surface mSurface;
TextureViewPreview(Context context, ViewGroup parent) { TextureViewPreview(Context context, ViewGroup parent) {
super(context, parent);
final View view = View.inflate(context, R.layout.texture_view, parent); // MATCH_PARENT final View view = View.inflate(context, R.layout.texture_view, parent); // MATCH_PARENT
mTextureView = (TextureView) view.findViewById(R.id.texture_view); mTextureView = (TextureView) view.findViewById(R.id.texture_view);
mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {

@ -42,6 +42,12 @@ import static com.flurgle.camerakit.CameraKit.Constants.FLASH_TORCH;
import static com.flurgle.camerakit.CameraKit.Constants.SESSION_TYPE_PICTURE; import static com.flurgle.camerakit.CameraKit.Constants.SESSION_TYPE_PICTURE;
import static com.flurgle.camerakit.CameraKit.Constants.SESSION_TYPE_VIDEO; import static com.flurgle.camerakit.CameraKit.Constants.SESSION_TYPE_VIDEO;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.UNSPECIFIED;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
/** /**
* The CameraView implements the LifecycleObserver interface for ease of use. To take advantage of * The CameraView implements the LifecycleObserver interface for ease of use. To take advantage of
* this, simply call the following from any LifecycleOwner: * this, simply call the following from any LifecycleOwner:
@ -188,79 +194,139 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
} }
private String ms(int mode) {
switch (mode) {
case AT_MOST: return "AT_MOST";
case EXACTLY: return "EXACTLY";
case UNSPECIFIED: return "UNSPECIFIED";
}
return null;
}
/** /**
* If adjustViewBounds was set AND one of the dimensions is set to WRAP_CONTENT, * Measuring is basically controlled by layout params width and height.
* CameraView will adjust that dimensions to fit the preview aspect ratio as returned by * The basic semantics are:
* {@link #getPreviewSize()}. *
* - MATCH_PARENT: CameraView should completely fill this dimension, even if this might mean
* not respecting the preview aspect ratio.
* - WRAP_CONTENT: CameraView should try to adapt this dimension to respect the preview
* aspect ratio.
* *
* If this is not true, the surface will adapt to the dimension specified in the layout file. * When both dimensions are MATCH_PARENT, CameraView will fill its
* Having fixed dimensions means that, very likely, what the user sees is different from what * parent no matter the preview. Thanks to what happens in {@link PreviewImpl}, this acts like
* the final picture will be. This is also due to what happens in {@link PreviewImpl#refreshScale()}. * a CENTER CROP scale type.
* *
* If this is a problem, you can use {@link #setCropOutput(boolean)} set to true. * When both dimensions are WRAP_CONTENT, CameraView will take the biggest dimensions that
* In that case, the final image will have the same aspect ratio of the preview. * fit the preview aspect ratio. This acts like a CENTER INSIDE scale type.
*/ */
@Override @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!mAdjustViewBounds) { Size previewSize = getPreviewSize();
Log.e(TAG, "onMeasure, adjustViewBounds=false"); if (previewSize == null) {
Log.e(TAG, "onMeasure, surface is not ready. Calling default behavior.");
super.onMeasure(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return; return;
} }
Size previewSize = getPreviewSize(); // Let's which dimensions need to be adapted.
if (previewSize == null) { // Early measure. int widthMode = MeasureSpec.getMode(widthMeasureSpec);
Log.e(TAG, "onMeasure, early measure"); int heightMode = MeasureSpec.getMode(heightMeasureSpec);
final int widthValue = MeasureSpec.getSize(widthMeasureSpec);
final int heightValue = MeasureSpec.getSize(heightMeasureSpec);
final boolean flip = mCameraImpl.shouldFlipSizes();
final float previewWidth = flip ? previewSize.getHeight() : previewSize.getWidth();
final float previewHeight = flip ? previewSize.getWidth() : previewSize.getHeight();
// If MATCH_PARENT is interpreted as AT_MOST, transform to EXACTLY
// to be consistent with our semantics.
final ViewGroup.LayoutParams lp = getLayoutParams();
if (widthMode == AT_MOST && lp.width == MATCH_PARENT) widthMode = EXACTLY;
if (heightMode == AT_MOST && lp.height == MATCH_PARENT) heightMode = EXACTLY;
Log.e(TAG, "onMeasure, requested dimensions are (" +
widthValue + "[" + ms(widthMode) + "]x" +
heightValue + "[" + ms(heightMode) + "])");
Log.e(TAG, "onMeasure, previewSize is (" + previewWidth + "x" + previewHeight + ")");
// If we have fixed dimensions (either 300dp or MATCH_PARENT), there's nothing we should do,
// other than respect it. The preview will eventually be cropped at the sides (by PreviewImpl scaling)
// except the case in which these fixed dimensions somehow fit exactly the preview aspect ratio.
if (widthMode == EXACTLY && heightMode == EXACTLY) {
Log.e(TAG, "onMeasure, both are MATCH_PARENT or fixed value. We adapt. This means CROP_INSIDE. " +
"(" + widthValue + "x" + heightValue + ")");
super.onMeasure(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return; return;
} }
boolean wwc = getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT; // If both dimensions are free, with no limits, then our size will be exactly the
boolean hwc = getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT; // preview size. This can happen rarely, for example in scrollable containers.
boolean flip = mCameraImpl.shouldFlipSizes(); if (widthMode == UNSPECIFIED && heightMode == UNSPECIFIED) {
float previewWidth = flip ? previewSize.getHeight() : previewSize.getWidth(); Log.e(TAG, "onMeasure, both are completely free. We respect that and extend to the whole preview size. " +
float previewHeight = flip ? previewSize.getWidth() : previewSize.getHeight(); "(" + previewWidth + "x" + previewHeight + ")");
float parentHeight = MeasureSpec.getSize(heightMeasureSpec); super.onMeasure(MeasureSpec.makeMeasureSpec((int) previewWidth, EXACTLY),
float parentWidth = MeasureSpec.getSize(widthMeasureSpec); // mode = AT_MOST MeasureSpec.makeMeasureSpec((int) previewHeight, EXACTLY));
Log.e(TAG, "onMeasure, parent size is "+new Size((int)parentWidth, (int)parentHeight)); // 1080x1794 return;
Log.e(TAG, "onMeasure, surface size is "+new Size((int)previewWidth, (int)previewHeight)); // 1600x1200 }
if (wwc && hwc) { // It sure now that at least one dimension can be determined (either because EXACTLY or AT_MOST).
// If both dimensions are WRAP_CONTENT, let's try to fit the preview size perfectly // This starts to seem a pleasant situation.
// without cropping.
// TODO: This should actually be a flag, like scaleMode, that replaces cropOutput and adjustViewBounds. // If one of the dimension is completely free, take the other and fit the ratio.
// This is like a fitCenter. // One of the two might be AT_MOST, but we use the value anyway.
float targetRatio = previewHeight / previewWidth; float ratio = previewHeight / previewWidth;
float currentRatio = parentHeight / parentWidth; if (widthMode == UNSPECIFIED || heightMode == UNSPECIFIED) {
if (currentRatio > targetRatio) { boolean freeWidth = widthMode == UNSPECIFIED;
// View is too tall. Must reduce height. int height, width;
int newHeight = (int) (parentWidth * targetRatio); if (freeWidth) {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY)); height = heightValue;
width = (int) (height / ratio);
} else { } else {
// View is too wide. Must reduce width. width = widthValue;
int newWidth = (int) (parentHeight / targetRatio); height = (int) (width * ratio);
super.onMeasure(MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY), heightMeasureSpec); }
} Log.e(TAG, "onMeasure, one dimension was free, we adapted it to fit the aspect ratio. " +
"(" + width + "x" + height + ")");
} else if (wwc) { super.onMeasure(MeasureSpec.makeMeasureSpec(width, EXACTLY),
// Legacy behavior, with just a WC dimension. This is dangerous because the final size MeasureSpec.makeMeasureSpec(height, EXACTLY));
// might be bigger than the available size, resulting in part of the surface getting cropped. return;
// Think for example of a 4:3 preview in a 16:9 screen, with width=MP and height=WC. }
// This is like a cropCenter.
float height = MeasureSpec.getSize(heightMeasureSpec);
float ratio = height / previewWidth;
int width = (int) (previewHeight * ratio);
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), heightMeasureSpec);
} else if (hwc) {
float width = MeasureSpec.getSize(widthMeasureSpec);
float ratio = width / previewHeight;
int height = (int) (previewWidth * ratio);
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
// At this point both dimensions are either AT_MOST-AT_MOST, EXACTLY-AT_MOST or AT_MOST-EXACTLY.
// Let's manage this sanely. If only one is EXACTLY, we can TRY to fit the aspect ratio,
// but it is not guaranteed to succeed. It depends on the AT_MOST value of the other dimensions.
if (widthMode == EXACTLY || heightMode == EXACTLY) {
boolean freeWidth = widthMode == AT_MOST;
int height, width;
if (freeWidth) {
height = heightValue;
width = Math.min((int) (height / ratio), widthValue);
} else { } else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = widthValue;
height = Math.min((int) (width * ratio), heightValue);
} }
Log.e(TAG, "onMeasure, one dimension was EXACTLY, another AT_MOST. We have TRIED to fit " +
"the aspect ratio, but it's not guaranteed. (" + width + "x" + height + ")");
super.onMeasure(MeasureSpec.makeMeasureSpec(width, EXACTLY),
MeasureSpec.makeMeasureSpec(height, EXACTLY));
return;
}
// Last case, AT_MOST and AT_MOST. Here we can SURELY fit the aspect ratio by filling one
// dimension and adapting the other.
int height, width;
float atMostRatio = heightValue / widthValue;
if (atMostRatio >= ratio) {
// We must reduce height.
width = widthValue;
height = (int) (width * ratio);
} else {
height = heightValue;
width = (int) (height / ratio);
}
Log.e(TAG, "onMeasure, both dimension were AT_MOST. We fit the preview aspect ratio. " +
"(" + width + "x" + height + ")");
super.onMeasure(MeasureSpec.makeMeasureSpec(width, EXACTLY),
MeasureSpec.makeMeasureSpec(height, EXACTLY));
} }
@ -759,23 +825,29 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
@Override @Override
public void onCameraOpened() { public void onCameraOpened() {
super.onCameraOpened();
if (mWrappedListener == null) return; if (mWrappedListener == null) return;
mWrappedListener.onCameraOpened(); mWrappedListener.onCameraOpened();
} }
@Override @Override
public void onCameraClosed() { public void onCameraClosed() {
super.onCameraClosed();
if (mWrappedListener == null) return; if (mWrappedListener == null) return;
mWrappedListener.onCameraClosed(); mWrappedListener.onCameraClosed();
} }
public void onCameraPreviewSizeChanged() {
// Camera preview size, as returned by getPreviewSize(), has changed.
// Request a layout pass for onMeasure() to do its stuff.
// Potentially this will change CameraView size, which changes Surface size,
// which triggers a new Preview size. But hopefully it will converge.
requestLayout();
}
@Override @Override
public void onPictureTaken(byte[] jpeg) { public void onPictureTaken(byte[] jpeg) {
super.onPictureTaken(jpeg);
if (mWrappedListener == null) return; if (mWrappedListener == null) return;
if (mCropOutput) { if (mCropOutput) {
// TODO cropOutput won't work if image is rotated (e.g. byte[] contains exif orientation).
AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight()); AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight());
mWrappedListener.onPictureTaken(new CenterCrop(jpeg, outputRatio, mJpegQuality).getJpeg()); mWrappedListener.onPictureTaken(new CenterCrop(jpeg, outputRatio, mJpegQuality).getJpeg());
} else { } else {
@ -783,7 +855,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
} }
} }
void processYuvImage(YuvImage yuv) { public void processYuvImage(YuvImage yuv) {
if (mWrappedListener == null) return; if (mWrappedListener == null) return;
if (mCropOutput) { if (mCropOutput) {
AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight()); AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight());
@ -797,7 +869,6 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
@Override @Override
public void onVideoTaken(File video) { public void onVideoTaken(File video) {
super.onVideoTaken(video);
mWrappedListener.onVideoTaken(video); mWrappedListener.onVideoTaken(video);
} }

@ -121,10 +121,9 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan
mCapturing = false; mCapturing = false;
long callbackTime = System.currentTimeMillis(); long callbackTime = System.currentTimeMillis();
Bitmap bitmap = BitmapFactory.decodeByteArray(jpeg, 0, jpeg.length); Bitmap bitmap = BitmapFactory.decodeByteArray(jpeg, 0, jpeg.length);
ResultHolder.dispose(); PicturePreviewActivity.setImage(bitmap);
ResultHolder.setImage(bitmap);
ResultHolder.setTimeToCallback(callbackTime - startTime);
Intent intent = new Intent(MainActivity.this, PicturePreviewActivity.class); Intent intent = new Intent(MainActivity.this, PicturePreviewActivity.class);
intent.putExtra("delay", callbackTime-startTime);
startActivity(intent); startActivity(intent);
} }
}); });
@ -144,9 +143,8 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan
public void onVideoTaken(File video) { public void onVideoTaken(File video) {
super.onVideoTaken(video); super.onVideoTaken(video);
mCapturing = false; mCapturing = false;
ResultHolder.dispose();
ResultHolder.setVideo(Uri.fromFile(video));
Intent intent = new Intent(MainActivity.this, VideoPreviewActivity.class); Intent intent = new Intent(MainActivity.this, VideoPreviewActivity.class);
intent.putExtra("video", Uri.fromFile(video));
startActivity(intent); startActivity(intent);
} }
}); });
@ -169,7 +167,6 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan
break; break;
case CameraKit.Constants.FACING_FRONT: case CameraKit.Constants.FACING_FRONT:
Toast.makeText(this, "Switched to front camera!", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Switched to front camera!", Toast.LENGTH_SHORT).show();
break; break;
} }
@ -307,6 +304,7 @@ public class MainActivity extends AppCompatActivity implements View.OnLayoutChan
break; break;
case R.id.heightMatchParent: case R.id.heightMatchParent:
// We are in a vertically scrolling container, match parent would not work at all.
height = parent.getHeight(); height = parent.getHeight();
break; break;
} }

@ -10,6 +10,8 @@ import android.widget.TextView;
import com.flurgle.camerakit.AspectRatio; import com.flurgle.camerakit.AspectRatio;
import com.flurgle.camerakit.Size; import com.flurgle.camerakit.Size;
import java.lang.ref.WeakReference;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
@ -30,13 +32,20 @@ public class PicturePreviewActivity extends Activity {
@BindView(R.id.captureLatency) @BindView(R.id.captureLatency)
TextView captureLatency; TextView captureLatency;
private static WeakReference<Bitmap> image;
public static void setImage(@Nullable Bitmap im) {
image = im != null ? new WeakReference<>(im) : null;
}
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_picture_preview); setContentView(R.layout.activity_picture_preview);
ButterKnife.bind(this); ButterKnife.bind(this);
Bitmap bitmap = ResultHolder.getImage(); long delay = getIntent().getLongExtra("delay", 0);
Bitmap bitmap = image == null ? null : image.get();
if (bitmap == null) { if (bitmap == null) {
finish(); finish();
return; return;
@ -51,7 +60,7 @@ public class PicturePreviewActivity extends Activity {
actualResolution.setText(bitmap.getWidth() + " x " + bitmap.getHeight()); actualResolution.setText(bitmap.getWidth() + " x " + bitmap.getHeight());
approxUncompressedSize.setText(getApproximateFileMegabytes(bitmap) + "MB"); approxUncompressedSize.setText(getApproximateFileMegabytes(bitmap) + "MB");
captureLatency.setText(ResultHolder.getTimeToCallback() + " milliseconds"); captureLatency.setText(delay + " milliseconds");
} }
private static float getApproximateFileMegabytes(Bitmap bitmap) { private static float getApproximateFileMegabytes(Bitmap bitmap) {

@ -1,50 +0,0 @@
package com.flurgle.camerakit.demo;
import android.graphics.Bitmap;
import android.net.Uri;
import android.support.annotation.Nullable;
import com.flurgle.camerakit.Size;
import java.lang.ref.WeakReference;
public class ResultHolder {
private static WeakReference<Bitmap> image;
private static Size nativeCaptureSize;
private static long timeToCallback;
private static Uri video;
public static Uri getVideo() {
return video;
}
public static void setVideo(Uri video) {
ResultHolder.video = video;
}
public static void setImage(@Nullable Bitmap image) {
ResultHolder.image = image != null ? new WeakReference<>(image) : null;
}
@Nullable
public static Bitmap getImage() {
return image != null ? image.get() : null;
}
public static void setTimeToCallback(long timeToCallback) {
ResultHolder.timeToCallback = timeToCallback;
}
public static long getTimeToCallback() {
return timeToCallback;
}
public static void dispose() {
setImage(null);
setVideo(null);
setTimeToCallback(0);
}
}

@ -33,12 +33,7 @@ public class VideoPreviewActivity extends Activity {
setContentView(R.layout.activity_video_preview); setContentView(R.layout.activity_video_preview);
ButterKnife.bind(this); ButterKnife.bind(this);
Uri videoUri = ResultHolder.getVideo(); Uri videoUri = getIntent().getParcelableExtra("video");
if (videoUri == null) {
finish();
return;
}
MediaController controller = new MediaController(this); MediaController controller = new MediaController(this);
controller.setAnchorView(videoView); controller.setAnchorView(videoView);
controller.setMediaPlayer(videoView); controller.setMediaPlayer(videoView);

Loading…
Cancel
Save