package com.flurgle.camerakit; import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.ContextWrapper; import android.content.pm.PackageManager; import android.content.res.TypedArray; import android.graphics.Rect; import android.graphics.YuvImage; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v4.hardware.display.DisplayManagerCompat; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.Display; import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; import java.io.ByteArrayOutputStream; import java.io.File; import java.util.ArrayList; import java.util.List; import static com.flurgle.camerakit.CameraKit.Constants.FACING_BACK; import static com.flurgle.camerakit.CameraKit.Constants.FACING_FRONT; import static com.flurgle.camerakit.CameraKit.Constants.FLASH_AUTO; import static com.flurgle.camerakit.CameraKit.Constants.FLASH_OFF; import static com.flurgle.camerakit.CameraKit.Constants.FLASH_ON; import static com.flurgle.camerakit.CameraKit.Constants.METHOD_STANDARD; import static com.flurgle.camerakit.CameraKit.Constants.PERMISSIONS_LAZY; import static com.flurgle.camerakit.CameraKit.Constants.PERMISSIONS_PICTURE; import static com.flurgle.camerakit.CameraKit.Constants.PERMISSIONS_STRICT; public class CameraView extends FrameLayout { @Facing private int mFacing; @Flash private int mFlash; @Focus private int mFocus; @Method private int mMethod; @Zoom private int mZoom; @Permissions private int mPermissions; @VideoQuality private int mVideoQuality; private int mJpegQuality; private boolean mCropOutput; private boolean mAdjustViewBounds; private CameraListenerMiddleWare mCameraListener; private DisplayOrientationDetector mDisplayOrientationDetector; private CameraImpl mCameraImpl; private PreviewImpl mPreviewImpl; public CameraView(@NonNull Context context) { super(context, null); } @SuppressWarnings("all") public CameraView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); if (attrs != null) { TypedArray a = context.getTheme().obtainStyledAttributes( attrs, R.styleable.CameraView, 0, 0); try { mFacing = a.getInteger(R.styleable.CameraView_ckFacing, CameraKit.Defaults.DEFAULT_FACING); mFlash = a.getInteger(R.styleable.CameraView_ckFlash, CameraKit.Defaults.DEFAULT_FLASH); mFocus = a.getInteger(R.styleable.CameraView_ckFocus, CameraKit.Defaults.DEFAULT_FOCUS); mMethod = a.getInteger(R.styleable.CameraView_ckMethod, CameraKit.Defaults.DEFAULT_METHOD); mZoom = a.getInteger(R.styleable.CameraView_ckZoom, CameraKit.Defaults.DEFAULT_ZOOM); mPermissions = a.getInteger(R.styleable.CameraView_ckPermissions, CameraKit.Defaults.DEFAULT_PERMISSIONS); mVideoQuality = a.getInteger(R.styleable.CameraView_ckVideoQuality, CameraKit.Defaults.DEFAULT_VIDEO_QUALITY); mJpegQuality = a.getInteger(R.styleable.CameraView_ckJpegQuality, CameraKit.Defaults.DEFAULT_JPEG_QUALITY); mCropOutput = a.getBoolean(R.styleable.CameraView_ckCropOutput, CameraKit.Defaults.DEFAULT_CROP_OUTPUT); mAdjustViewBounds = a.getBoolean(R.styleable.CameraView_android_adjustViewBounds, CameraKit.Defaults.DEFAULT_ADJUST_VIEW_BOUNDS); } finally { a.recycle(); } } mCameraListener = new CameraListenerMiddleWare(); mPreviewImpl = new TextureViewPreview(context, this); mCameraImpl = new Camera1(mCameraListener, mPreviewImpl); setFacing(mFacing); setFlash(mFlash); setFocus(mFocus); setMethod(mMethod); setZoom(mZoom); setPermissions(mPermissions); setVideoQuality(mVideoQuality); mDisplayOrientationDetector = new DisplayOrientationDetector(context) { @Override public void onDisplayOrientationChanged(int displayOrientation) { mCameraImpl.setDisplayOrientation(displayOrientation); mPreviewImpl.setDisplayOrientation(displayOrientation); } }; final FocusMarkerLayout focusMarkerLayout = new FocusMarkerLayout(getContext()); addView(focusMarkerLayout); focusMarkerLayout.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent motionEvent) { int action = motionEvent.getAction(); if (motionEvent.getAction() == MotionEvent.ACTION_UP && mFocus == CameraKit.Constants.FOCUS_TAP_WITH_MARKER) { focusMarkerLayout.focus(motionEvent.getX(), motionEvent.getY()); } mPreviewImpl.getView().dispatchTouchEvent(motionEvent); return true; } }); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mDisplayOrientationDetector.enable( ViewCompat.isAttachedToWindow(this) ? DisplayManagerCompat.getInstance(getContext()).getDisplay(Display.DEFAULT_DISPLAY) : null ); } @Override protected void onDetachedFromWindow() { mDisplayOrientationDetector.disable(); super.onDetachedFromWindow(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mAdjustViewBounds) { Size previewSize = getPreviewSize(); if (previewSize != null) { if (getLayoutParams().width == LayoutParams.WRAP_CONTENT) { int height = MeasureSpec.getSize(heightMeasureSpec); float ratio = (float) height / (float) previewSize.getWidth(); int width = (int) (previewSize.getHeight() * ratio); super.onMeasure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), heightMeasureSpec ); return; } else if (getLayoutParams().height == LayoutParams.WRAP_CONTENT) { int width = MeasureSpec.getSize(widthMeasureSpec); float ratio = (float) width / (float) previewSize.getHeight(); int height = (int) (previewSize.getWidth() * ratio); super.onMeasure( widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY) ); return; } } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); return; } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } public void start() { int cameraCheck = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA); int audioCheck = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.RECORD_AUDIO); switch (mPermissions) { case PERMISSIONS_STRICT: if (cameraCheck != PackageManager.PERMISSION_GRANTED || audioCheck != PackageManager.PERMISSION_GRANTED) { requestPermissions(true, true); return; } break; case PERMISSIONS_LAZY: if (cameraCheck != PackageManager.PERMISSION_GRANTED) { requestPermissions(true, true); return; } break; case PERMISSIONS_PICTURE: if (cameraCheck != PackageManager.PERMISSION_GRANTED) { requestPermissions(true, false); return; } break; } new Thread(new Runnable() { @Override public void run() { mCameraImpl.start(); } }).start(); } public void stop() { mCameraImpl.stop(); } public void setFacing(@Facing final int facing) { this.mFacing = facing; new Thread(new Runnable() { @Override public void run() { mCameraImpl.setFacing(facing); } }).start(); } public void setFlash(@Flash int flash) { this.mFlash = flash; mCameraImpl.setFlash(flash); } public void setFocus(@Focus int focus) { this.mFocus = focus; if (this.mFocus == CameraKit.Constants.FOCUS_TAP_WITH_MARKER) { mCameraImpl.setFocus(CameraKit.Constants.FOCUS_TAP); return; } mCameraImpl.setFocus(mFocus); } public void setMethod(@Method int method) { this.mMethod = method; mCameraImpl.setMethod(mMethod); } public void setZoom(@Zoom int zoom) { this.mZoom = zoom; mCameraImpl.setZoom(mZoom); } public void setPermissions(@Permissions int permissions) { this.mPermissions = permissions; } public void setVideoQuality(@VideoQuality int videoQuality) { this.mVideoQuality = videoQuality; mCameraImpl.setVideoQuality(mVideoQuality); } public void setJpegQuality(int jpegQuality) { this.mJpegQuality = jpegQuality; } public void setCropOutput(boolean cropOutput) { this.mCropOutput = cropOutput; } @Facing public int toggleFacing() { switch (mFacing) { case FACING_BACK: setFacing(FACING_FRONT); break; case FACING_FRONT: setFacing(FACING_BACK); break; } return mFacing; } @Flash public int toggleFlash() { switch (mFlash) { case FLASH_OFF: setFlash(FLASH_ON); break; case FLASH_ON: setFlash(FLASH_AUTO); break; case FLASH_AUTO: setFlash(FLASH_OFF); break; } return mFlash; } public void setCameraListener(CameraListener cameraListener) { this.mCameraListener.setCameraListener(cameraListener); } public void captureImage() { mCameraImpl.captureImage(); } public void startRecordingVideo() { mCameraImpl.startVideo(); } public void stopRecordingVideo() { mCameraImpl.endVideo(); } public Size getPreviewSize() { return mCameraImpl != null ? mCameraImpl.getPreviewResolution() : null; } public Size getCaptureSize() { return mCameraImpl != null ? mCameraImpl.getCaptureResolution() : null; } private void requestPermissions(boolean requestCamera, boolean requestAudio) { Activity activity = null; Context context = getContext(); while (context instanceof ContextWrapper) { if (context instanceof Activity) { activity = (Activity) context; } context = ((ContextWrapper) context).getBaseContext(); } List<String> permissions = new ArrayList<>(); if (requestCamera) permissions.add(Manifest.permission.CAMERA); if (requestAudio) permissions.add(Manifest.permission.RECORD_AUDIO); if (activity != null) { ActivityCompat.requestPermissions( activity, permissions.toArray(new String[permissions.size()]), CameraKit.Constants.PERMISSION_REQUEST_CAMERA); } } private class CameraListenerMiddleWare extends CameraListener { private CameraListener mCameraListener; @Override public void onCameraOpened() { super.onCameraOpened(); getCameraListener().onCameraOpened(); } @Override public void onCameraClosed() { super.onCameraClosed(); getCameraListener().onCameraClosed(); } @Override public void onPictureTaken(byte[] jpeg) { super.onPictureTaken(jpeg); if (mCropOutput) { int width = mMethod == METHOD_STANDARD ? mCameraImpl.getCaptureResolution().getWidth() : mCameraImpl.getPreviewResolution().getWidth(); int height = mMethod == METHOD_STANDARD ? mCameraImpl.getCaptureResolution().getHeight() : mCameraImpl.getPreviewResolution().getHeight(); AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight()); getCameraListener().onPictureTaken(new CenterCrop(jpeg, outputRatio, mJpegQuality).getJpeg()); } else { getCameraListener().onPictureTaken(jpeg); } } @Override public void onPictureTaken(YuvImage yuv) { super.onPictureTaken(yuv); if (mCropOutput) { AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight()); getCameraListener().onPictureTaken(new CenterCrop(yuv, outputRatio, mJpegQuality).getJpeg()); } else { ByteArrayOutputStream out = new ByteArrayOutputStream(); yuv.compressToJpeg(new Rect(0, 0, yuv.getWidth(), yuv.getHeight()), mJpegQuality, out); getCameraListener().onPictureTaken(out.toByteArray()); } } @Override public void onVideoTaken(File video) { super.onVideoTaken(video); getCameraListener().onVideoTaken(video); } public void setCameraListener(@Nullable CameraListener cameraListener) { this.mCameraListener = cameraListener; } @NonNull public CameraListener getCameraListener() { return mCameraListener != null ? mCameraListener : new CameraListener() { }; } } }