convert Preview to kotlin
This commit is contained in:
parent
9576aa0f51
commit
1095ed56e5
|
@ -1,666 +0,0 @@
|
|||
package com.simplemobiletools.camera;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.Camera;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.MediaRecorder;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.support.v4.provider.DocumentFile;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.simplemobiletools.camera.activities.MainActivity;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class Preview extends ViewGroup
|
||||
implements SurfaceHolder.Callback, View.OnTouchListener, View.OnClickListener, MediaScannerConnection.OnScanCompletedListener {
|
||||
public static final int PHOTO_PREVIEW_LENGTH = 1000;
|
||||
private static final String TAG = Preview.class.getSimpleName();
|
||||
private static final int FOCUS_AREA_SIZE = 100;
|
||||
|
||||
private static SurfaceHolder mSurfaceHolder;
|
||||
private static Camera mCamera;
|
||||
private static List<Camera.Size> mSupportedPreviewSizes;
|
||||
private static SurfaceView mSurfaceView;
|
||||
private static Camera.Size mPreviewSize;
|
||||
private static MainActivity mActivity;
|
||||
private static Camera.Parameters mParameters;
|
||||
private static PreviewListener mCallback;
|
||||
private static MediaRecorder mRecorder;
|
||||
private static String mCurrVideoPath;
|
||||
private static Point mScreenSize;
|
||||
private static Uri mTargetUri;
|
||||
private static Context mContext;
|
||||
private static ScaleGestureDetector mScaleGestureDetector;
|
||||
private static List<Integer> mZoomRatios;
|
||||
private static Config mConfig;
|
||||
|
||||
private static boolean mCanTakePicture;
|
||||
private static boolean mIsFlashEnabled;
|
||||
private static boolean mIsRecording;
|
||||
private static boolean mIsVideoMode;
|
||||
private static boolean mIsSurfaceCreated;
|
||||
private static boolean mSwitchToVideoAsap;
|
||||
private static boolean mSetupPreviewAfterMeasure;
|
||||
private static boolean mForceAspectRatio;
|
||||
private static boolean mWasZooming;
|
||||
private static int mLastClickX;
|
||||
private static int mLastClickY;
|
||||
private static int mInitVideoRotation;
|
||||
private static int mCurrCameraId;
|
||||
private static int mMaxZoom;
|
||||
|
||||
public Preview(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public Preview(MainActivity activity, SurfaceView surfaceView, PreviewListener previewListener) {
|
||||
super(activity);
|
||||
|
||||
mActivity = activity;
|
||||
mCallback = previewListener;
|
||||
mSurfaceView = surfaceView;
|
||||
mSurfaceHolder = mSurfaceView.getHolder();
|
||||
mSurfaceHolder.addCallback(this);
|
||||
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
|
||||
mCanTakePicture = false;
|
||||
mSurfaceView.setOnTouchListener(this);
|
||||
mSurfaceView.setOnClickListener(this);
|
||||
mIsFlashEnabled = false;
|
||||
mIsVideoMode = false;
|
||||
mIsSurfaceCreated = false;
|
||||
mSetupPreviewAfterMeasure = false;
|
||||
mCurrVideoPath = "";
|
||||
mScreenSize = Utils.Companion.getScreenSize(mActivity);
|
||||
mContext = getContext();
|
||||
initGestureDetector();
|
||||
}
|
||||
|
||||
public void trySwitchToVideo() {
|
||||
if (mIsSurfaceCreated) {
|
||||
initRecorder();
|
||||
} else {
|
||||
mSwitchToVideoAsap = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setCamera(int cameraId) {
|
||||
mCurrCameraId = cameraId;
|
||||
Camera newCamera;
|
||||
try {
|
||||
newCamera = Camera.open(cameraId);
|
||||
mCallback.setIsCameraAvailable(true);
|
||||
} catch (Exception e) {
|
||||
Utils.Companion.showToast(mContext, R.string.camera_open_error);
|
||||
Log.e(TAG, "setCamera open " + e.getMessage());
|
||||
mCallback.setIsCameraAvailable(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mCamera == newCamera) {
|
||||
return false;
|
||||
}
|
||||
|
||||
releaseCamera();
|
||||
mCamera = newCamera;
|
||||
if (mCamera != null) {
|
||||
mParameters = mCamera.getParameters();
|
||||
mMaxZoom = mParameters.getMaxZoom();
|
||||
mZoomRatios = mParameters.getZoomRatios();
|
||||
mSupportedPreviewSizes = mParameters.getSupportedPreviewSizes();
|
||||
Collections.sort(mSupportedPreviewSizes, new SizesComparator());
|
||||
requestLayout();
|
||||
invalidate();
|
||||
mSetupPreviewAfterMeasure = true;
|
||||
|
||||
final List<String> focusModes = mParameters.getSupportedFocusModes();
|
||||
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
|
||||
mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
|
||||
|
||||
final int rotation = Utils.Companion.getPreviewRotation(mActivity, cameraId);
|
||||
mCamera.setDisplayOrientation(rotation);
|
||||
mCamera.setParameters(mParameters);
|
||||
|
||||
if (mCanTakePicture) {
|
||||
try {
|
||||
mCamera.setPreviewDisplay(mSurfaceHolder);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "setCamera setPreviewDisplay " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mCallback.setFlashAvailable(Utils.Companion.hasFlash(mCamera));
|
||||
}
|
||||
|
||||
if (mIsVideoMode) {
|
||||
initRecorder();
|
||||
}
|
||||
|
||||
mConfig = Config.Companion.newInstance(mContext);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setTargetUri(Uri uri) {
|
||||
mTargetUri = uri;
|
||||
}
|
||||
|
||||
private void initGestureDetector() {
|
||||
mScaleGestureDetector = new ScaleGestureDetector(mContext, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector detector) {
|
||||
int zoomFactor = mParameters.getZoom();
|
||||
float zoomRatio = mZoomRatios.get(zoomFactor) / 100.f;
|
||||
zoomRatio *= detector.getScaleFactor();
|
||||
|
||||
int newZoomFactor = zoomFactor;
|
||||
if (zoomRatio <= 1.f) {
|
||||
newZoomFactor = 0;
|
||||
} else if (zoomRatio >= mZoomRatios.get(mMaxZoom) / 100.f) {
|
||||
newZoomFactor = mMaxZoom;
|
||||
} else {
|
||||
if (detector.getScaleFactor() > 1.f) {
|
||||
for (int i = zoomFactor; i < mZoomRatios.size(); i++) {
|
||||
if (mZoomRatios.get(i) / 100.0f >= zoomRatio) {
|
||||
newZoomFactor = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = zoomFactor; i >= 0; i--) {
|
||||
if (mZoomRatios.get(i) / 100.0f <= zoomRatio) {
|
||||
newZoomFactor = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newZoomFactor = Math.max(newZoomFactor, 0);
|
||||
newZoomFactor = Math.min(mMaxZoom, newZoomFactor);
|
||||
|
||||
mParameters.setZoom(newZoomFactor);
|
||||
if (mCamera != null)
|
||||
mCamera.setParameters(mParameters);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||
super.onScaleEnd(detector);
|
||||
mWasZooming = true;
|
||||
mSurfaceView.setSoundEffectsEnabled(false);
|
||||
mParameters.setFocusAreas(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void takePicture() {
|
||||
if (mCanTakePicture) {
|
||||
int rotation = Utils.Companion.getMediaRotation(mActivity, mCurrCameraId);
|
||||
rotation += Utils.Companion.compensateDeviceRotation(mCurrCameraId, mCallback.getCurrentOrientation());
|
||||
|
||||
/*final Camera.Size maxSize = getOptimalPictureSize();
|
||||
mParameters.setPictureSize(maxSize.width, maxSize.height);*/
|
||||
mParameters.setRotation(rotation % 360);
|
||||
|
||||
if (mConfig.isSoundEnabled()) {
|
||||
final AudioManager audioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
final int volume = audioManager.getStreamVolume(AudioManager.STREAM_SYSTEM);
|
||||
if (volume != 0) {
|
||||
final MediaPlayer mp = MediaPlayer.create(getContext(), Uri.parse("file:///system/media/audio/ui/camera_click.ogg"));
|
||||
if (mp != null)
|
||||
mp.start();
|
||||
}
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
mCamera.enableShutterSound(false);
|
||||
}
|
||||
|
||||
mCamera.setParameters(mParameters);
|
||||
mCamera.takePicture(null, null, takePictureCallback);
|
||||
}
|
||||
mCanTakePicture = false;
|
||||
}
|
||||
|
||||
private Camera.PictureCallback takePictureCallback = new Camera.PictureCallback() {
|
||||
@Override
|
||||
public void onPictureTaken(byte[] data, Camera cam) {
|
||||
if (mConfig.isShowPreviewEnabled()) {
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
resumePreview();
|
||||
}
|
||||
}, PHOTO_PREVIEW_LENGTH);
|
||||
} else {
|
||||
resumePreview();
|
||||
}
|
||||
|
||||
new PhotoProcessor(mActivity, mTargetUri).execute(data);
|
||||
}
|
||||
};
|
||||
|
||||
private void resumePreview() {
|
||||
if (mCamera != null) {
|
||||
mCamera.startPreview();
|
||||
}
|
||||
|
||||
mCanTakePicture = true;
|
||||
}
|
||||
|
||||
public List<Camera.Size> getSupportedVideoSizes() {
|
||||
if (mParameters.getSupportedVideoSizes() != null) {
|
||||
return mParameters.getSupportedVideoSizes();
|
||||
} else {
|
||||
return mParameters.getSupportedPreviewSizes();
|
||||
}
|
||||
}
|
||||
|
||||
private void focusArea(final boolean takePictureAfter) {
|
||||
if (mCamera == null)
|
||||
return;
|
||||
|
||||
mCamera.cancelAutoFocus();
|
||||
final Rect focusRect = calculateFocusArea(mLastClickX, mLastClickY);
|
||||
if (mParameters.getMaxNumFocusAreas() > 0) {
|
||||
final List<Camera.Area> focusAreas = new ArrayList<>(1);
|
||||
focusAreas.add(new Camera.Area(focusRect, 1000));
|
||||
mParameters.setFocusAreas(focusAreas);
|
||||
mCallback.drawFocusRect(mLastClickX, mLastClickY);
|
||||
}
|
||||
|
||||
mCamera.setParameters(mParameters);
|
||||
mCamera.autoFocus(new Camera.AutoFocusCallback() {
|
||||
@Override
|
||||
public void onAutoFocus(boolean success, Camera camera) {
|
||||
camera.cancelAutoFocus();
|
||||
final List<String> focusModes = mParameters.getSupportedFocusModes();
|
||||
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
|
||||
mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
|
||||
|
||||
camera.setParameters(mParameters);
|
||||
if (takePictureAfter) {
|
||||
takePicture();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Rect calculateFocusArea(float x, float y) {
|
||||
int left = Float.valueOf((x / mSurfaceView.getWidth()) * 2000 - 1000).intValue();
|
||||
int top = Float.valueOf((y / mSurfaceView.getHeight()) * 2000 - 1000).intValue();
|
||||
|
||||
int tmp = left;
|
||||
left = top;
|
||||
top = -tmp;
|
||||
|
||||
final int rectLeft = Math.max(left - FOCUS_AREA_SIZE / 2, -1000);
|
||||
final int rectTop = Math.max(top - FOCUS_AREA_SIZE / 2, -1000);
|
||||
final int rectRight = Math.min(left + FOCUS_AREA_SIZE / 2, 1000);
|
||||
final int rectBottom = Math.min(top + FOCUS_AREA_SIZE / 2, 1000);
|
||||
return new Rect(rectLeft, rectTop, rectRight, rectBottom);
|
||||
}
|
||||
|
||||
public void releaseCamera() {
|
||||
stopRecording();
|
||||
|
||||
if (mCamera != null) {
|
||||
mCamera.stopPreview();
|
||||
mCamera.release();
|
||||
mCamera = null;
|
||||
}
|
||||
|
||||
cleanupRecorder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
mIsSurfaceCreated = true;
|
||||
try {
|
||||
if (mCamera != null) {
|
||||
mCamera.setPreviewDisplay(mSurfaceHolder);
|
||||
}
|
||||
|
||||
if (mSwitchToVideoAsap)
|
||||
initRecorder();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "surfaceCreated IOException " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
mIsSurfaceCreated = true;
|
||||
|
||||
if (mIsVideoMode) {
|
||||
initRecorder();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
mIsSurfaceCreated = false;
|
||||
if (mCamera != null) {
|
||||
mCamera.stopPreview();
|
||||
}
|
||||
|
||||
cleanupRecorder();
|
||||
}
|
||||
|
||||
private void setupPreview() {
|
||||
mCanTakePicture = true;
|
||||
if (mCamera != null && mPreviewSize != null) {
|
||||
mParameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
|
||||
mCamera.setParameters(mParameters);
|
||||
mCamera.startPreview();
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupRecorder() {
|
||||
if (mRecorder != null) {
|
||||
if (mIsRecording) {
|
||||
stopRecording();
|
||||
}
|
||||
|
||||
mRecorder.release();
|
||||
mRecorder = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
|
||||
}
|
||||
|
||||
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int width, int height) {
|
||||
Camera.Size result = null;
|
||||
for (Camera.Size size : sizes) {
|
||||
if (size.width <= width && size.height <= height) {
|
||||
if (result == null) {
|
||||
result = size;
|
||||
} else {
|
||||
int resultArea = result.width * result.height;
|
||||
int newArea = size.width * size.height;
|
||||
|
||||
if (newArea > resultArea) {
|
||||
result = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
setMeasuredDimension(mScreenSize.x, mScreenSize.y);
|
||||
|
||||
if (mSupportedPreviewSizes != null) {
|
||||
// for simplicity lets assume that most displays are 16:9 and the remaining ones are 4:3
|
||||
// always set 16:9 for videos as many devices support 4:3 only in low quality
|
||||
if (mForceAspectRatio || mIsVideoMode) {
|
||||
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, mScreenSize.y, mScreenSize.x);
|
||||
} else {
|
||||
final int newRatioHeight = (int) (mScreenSize.x * ((double) 4 / 3));
|
||||
setMeasuredDimension(mScreenSize.x, newRatioHeight);
|
||||
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, newRatioHeight, mScreenSize.x);
|
||||
}
|
||||
final LayoutParams lp = mSurfaceView.getLayoutParams();
|
||||
|
||||
// make sure to occupy whole width in every case
|
||||
if (mScreenSize.x > mPreviewSize.height) {
|
||||
final float ratio = (float) mScreenSize.x / mPreviewSize.height;
|
||||
lp.width = (int) (mPreviewSize.height * ratio);
|
||||
if (mForceAspectRatio || mIsVideoMode) {
|
||||
lp.height = mScreenSize.y;
|
||||
} else {
|
||||
lp.height = (int) (mPreviewSize.width * ratio);
|
||||
}
|
||||
} else {
|
||||
lp.width = mPreviewSize.height;
|
||||
lp.height = mPreviewSize.width;
|
||||
}
|
||||
|
||||
if (mSetupPreviewAfterMeasure) {
|
||||
mSetupPreviewAfterMeasure = false;
|
||||
if (mCamera != null)
|
||||
mCamera.stopPreview();
|
||||
|
||||
setupPreview();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
mLastClickX = (int) event.getX();
|
||||
mLastClickY = (int) event.getY();
|
||||
|
||||
if (mMaxZoom > 0)
|
||||
mScaleGestureDetector.onTouchEvent(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void enableFlash() {
|
||||
mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
|
||||
mCamera.setParameters(mParameters);
|
||||
mIsFlashEnabled = true;
|
||||
}
|
||||
|
||||
public void disableFlash() {
|
||||
mIsFlashEnabled = false;
|
||||
mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
|
||||
mCamera.setParameters(mParameters);
|
||||
}
|
||||
|
||||
public void initPhotoMode() {
|
||||
stopRecording();
|
||||
cleanupRecorder();
|
||||
mIsRecording = false;
|
||||
mIsVideoMode = false;
|
||||
recheckAspectRatio();
|
||||
}
|
||||
|
||||
private void recheckAspectRatio() {
|
||||
if (!mForceAspectRatio) {
|
||||
mSetupPreviewAfterMeasure = true;
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
// VIDEO RECORDING
|
||||
public boolean initRecorder() {
|
||||
if (mCamera == null || mRecorder != null || !mIsSurfaceCreated)
|
||||
return false;
|
||||
|
||||
mSwitchToVideoAsap = false;
|
||||
final List<Camera.Size> previewSizes = mParameters.getSupportedPreviewSizes();
|
||||
Collections.sort(previewSizes, new SizesComparator());
|
||||
Camera.Size preferred = previewSizes.get(0);
|
||||
|
||||
mParameters.setPreviewSize(preferred.width, preferred.height);
|
||||
mCamera.setParameters(mParameters);
|
||||
|
||||
mIsRecording = false;
|
||||
mIsVideoMode = true;
|
||||
recheckAspectRatio();
|
||||
mRecorder = new MediaRecorder();
|
||||
mRecorder.setCamera(mCamera);
|
||||
mRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
|
||||
mRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
|
||||
|
||||
mCurrVideoPath = Utils.Companion.getOutputMediaFile(mContext, false);
|
||||
if (mCurrVideoPath.isEmpty()) {
|
||||
Utils.Companion.showToast(mContext, R.string.video_creating_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*final Camera.Size videoSize = getOptimalVideoSize();
|
||||
final CamcorderProfile cpHigh = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
|
||||
cpHigh.videoFrameWidth = videoSize.width;
|
||||
cpHigh.videoFrameHeight = videoSize.height;
|
||||
mRecorder.setProfile(cpHigh);*/
|
||||
|
||||
if (Utils.Companion.needsStupidWritePermissions(getContext(), mCurrVideoPath)) {
|
||||
if (mConfig.getTreeUri().isEmpty()) {
|
||||
Utils.Companion.showToast(mContext, R.string.save_error_internal_storage);
|
||||
mConfig.setSavePhotosFolder(Environment.getExternalStorageDirectory().toString());
|
||||
releaseCamera();
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
DocumentFile document = Utils.Companion.getFileDocument(getContext(), mCurrVideoPath, mConfig.getTreeUri());
|
||||
document = document.createFile("", mCurrVideoPath.substring(mCurrVideoPath.lastIndexOf('/') + 1));
|
||||
final Uri uri = document.getUri();
|
||||
final ParcelFileDescriptor fileDescriptor = getContext().getContentResolver().openFileDescriptor(uri, "rw");
|
||||
mRecorder.setOutputFile(fileDescriptor.getFileDescriptor());
|
||||
} catch (Exception e) {
|
||||
setupFailed(e);
|
||||
}
|
||||
} else {
|
||||
mRecorder.setOutputFile(mCurrVideoPath);
|
||||
}
|
||||
mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
|
||||
|
||||
int rotation = Utils.Companion.getFinalRotation(mActivity, mCurrCameraId, mCallback.getCurrentOrientation());
|
||||
mInitVideoRotation = rotation;
|
||||
mRecorder.setOrientationHint(rotation);
|
||||
|
||||
try {
|
||||
mRecorder.prepare();
|
||||
} catch (Exception e) {
|
||||
setupFailed(e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setupFailed(Exception e) {
|
||||
Utils.Companion.showToast(mContext, R.string.video_setup_error);
|
||||
Log.e(TAG, "initRecorder " + e.getMessage());
|
||||
releaseCamera();
|
||||
}
|
||||
|
||||
public boolean toggleRecording() {
|
||||
if (mIsRecording) {
|
||||
stopRecording();
|
||||
initRecorder();
|
||||
} else {
|
||||
startRecording();
|
||||
}
|
||||
return mIsRecording;
|
||||
}
|
||||
|
||||
private void startRecording() {
|
||||
if (mInitVideoRotation != Utils.Companion.getFinalRotation(mActivity, mCurrCameraId, mCallback.getCurrentOrientation())) {
|
||||
cleanupRecorder();
|
||||
initRecorder();
|
||||
}
|
||||
|
||||
try {
|
||||
mCamera.unlock();
|
||||
toggleShutterSound(true);
|
||||
mRecorder.start();
|
||||
toggleShutterSound(false);
|
||||
mIsRecording = true;
|
||||
} catch (Exception e) {
|
||||
Utils.Companion.showToast(mContext, R.string.video_setup_error);
|
||||
Log.e(TAG, "toggleRecording " + e.getMessage());
|
||||
releaseCamera();
|
||||
}
|
||||
}
|
||||
|
||||
private void stopRecording() {
|
||||
if (mRecorder != null && mIsRecording) {
|
||||
try {
|
||||
toggleShutterSound(true);
|
||||
mRecorder.stop();
|
||||
final String[] paths = {mCurrVideoPath};
|
||||
MediaScannerConnection.scanFile(mContext, paths, null, this);
|
||||
} catch (RuntimeException e) {
|
||||
toggleShutterSound(false);
|
||||
new File(mCurrVideoPath).delete();
|
||||
Utils.Companion.showToast(mContext, R.string.video_saving_error);
|
||||
Log.e(TAG, "stopRecording " + e.getMessage());
|
||||
mRecorder = null;
|
||||
mIsRecording = false;
|
||||
releaseCamera();
|
||||
}
|
||||
}
|
||||
|
||||
mRecorder = null;
|
||||
mIsRecording = false;
|
||||
|
||||
final File file = new File(mCurrVideoPath);
|
||||
if (file.exists() && file.length() == 0) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleShutterSound(Boolean mute) {
|
||||
if (!mConfig.isSoundEnabled()) {
|
||||
((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)).setStreamMute(AudioManager.STREAM_SYSTEM, mute);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (!mWasZooming)
|
||||
focusArea(false);
|
||||
|
||||
mWasZooming = false;
|
||||
mSurfaceView.setSoundEffectsEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScanCompleted(String path, Uri uri) {
|
||||
mCallback.videoSaved(uri);
|
||||
toggleShutterSound(false);
|
||||
}
|
||||
|
||||
private static class SizesComparator implements Comparator<Camera.Size>, Serializable {
|
||||
private static final long serialVersionUID = 5431278455314658485L;
|
||||
|
||||
@Override
|
||||
public int compare(final Camera.Size a, final Camera.Size b) {
|
||||
return b.width * b.height - a.width * a.height;
|
||||
}
|
||||
}
|
||||
|
||||
public interface PreviewListener {
|
||||
void setFlashAvailable(boolean available);
|
||||
|
||||
void setIsCameraAvailable(boolean available);
|
||||
|
||||
int getCurrentOrientation();
|
||||
|
||||
void videoSaved(Uri uri);
|
||||
|
||||
void drawFocusRect(int x, int y);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,636 @@
|
|||
package com.simplemobiletools.camera
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import android.hardware.Camera
|
||||
import android.media.AudioManager
|
||||
import android.media.MediaPlayer
|
||||
import android.media.MediaRecorder
|
||||
import android.media.MediaScannerConnection
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import com.simplemobiletools.camera.activities.MainActivity
|
||||
import com.simplemobiletools.commons.extensions.scanPaths
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
|
||||
class Preview : ViewGroup, SurfaceHolder.Callback, View.OnTouchListener, View.OnClickListener, MediaScannerConnection.OnScanCompletedListener {
|
||||
companion object {
|
||||
val PHOTO_PREVIEW_LENGTH = 1000
|
||||
private val TAG = Preview::class.java.simpleName
|
||||
private val FOCUS_AREA_SIZE = 100
|
||||
|
||||
lateinit var mSurfaceHolder: SurfaceHolder
|
||||
lateinit var mSurfaceView: SurfaceView
|
||||
lateinit var mActivity: MainActivity
|
||||
lateinit var mCallback: PreviewListener
|
||||
lateinit var mScreenSize: Point
|
||||
private var mCamera: Camera? = null
|
||||
private var mSupportedPreviewSizes: List<Camera.Size>? = null
|
||||
private var mPreviewSize: Camera.Size? = null
|
||||
private var mParameters: Camera.Parameters? = null
|
||||
private var mRecorder: MediaRecorder? = null
|
||||
private var mCurrVideoPath: String? = null
|
||||
private var mTargetUri: Uri? = null
|
||||
private var mScaleGestureDetector: ScaleGestureDetector? = null
|
||||
private var mZoomRatios: List<Int>? = null
|
||||
private var mConfig: Config? = null
|
||||
|
||||
private var mCanTakePicture: Boolean = false
|
||||
private var mIsFlashEnabled: Boolean = false
|
||||
private var mIsRecording: Boolean = false
|
||||
private var mIsVideoMode: Boolean = false
|
||||
private var mIsSurfaceCreated: Boolean = false
|
||||
private var mSwitchToVideoAsap: Boolean = false
|
||||
private var mSetupPreviewAfterMeasure: Boolean = false
|
||||
private val mForceAspectRatio: Boolean = false
|
||||
private var mWasZooming: Boolean = false
|
||||
private var mLastClickX: Int = 0
|
||||
private var mLastClickY: Int = 0
|
||||
private var mInitVideoRotation: Int = 0
|
||||
private var mCurrCameraId: Int = 0
|
||||
private var mMaxZoom: Int = 0
|
||||
}
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(activity: MainActivity, surfaceView: SurfaceView, previewListener: PreviewListener) : super(activity) {
|
||||
mActivity = activity
|
||||
mCallback = previewListener
|
||||
mSurfaceView = surfaceView
|
||||
mSurfaceHolder = mSurfaceView.holder
|
||||
mSurfaceHolder.addCallback(this)
|
||||
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
|
||||
mCanTakePicture = false
|
||||
mSurfaceView.setOnTouchListener(this)
|
||||
mSurfaceView.setOnClickListener(this)
|
||||
mIsFlashEnabled = false
|
||||
mIsVideoMode = false
|
||||
mIsSurfaceCreated = false
|
||||
mSetupPreviewAfterMeasure = false
|
||||
mCurrVideoPath = ""
|
||||
mScreenSize = Utils.getScreenSize(mActivity)
|
||||
initGestureDetector()
|
||||
}
|
||||
|
||||
fun trySwitchToVideo() {
|
||||
if (mIsSurfaceCreated) {
|
||||
initRecorder()
|
||||
} else {
|
||||
mSwitchToVideoAsap = true
|
||||
}
|
||||
}
|
||||
|
||||
fun setCamera(cameraId: Int): Boolean {
|
||||
mCurrCameraId = cameraId
|
||||
val newCamera: Camera
|
||||
try {
|
||||
newCamera = Camera.open(cameraId)
|
||||
mCallback.setIsCameraAvailable(true)
|
||||
} catch (e: Exception) {
|
||||
Utils.showToast(mActivity, R.string.camera_open_error)
|
||||
Log.e(TAG, "setCamera open " + e.message)
|
||||
mCallback.setIsCameraAvailable(false)
|
||||
return false
|
||||
}
|
||||
|
||||
if (mCamera === newCamera) {
|
||||
return false
|
||||
}
|
||||
|
||||
releaseCamera()
|
||||
mCamera = newCamera
|
||||
if (mCamera != null) {
|
||||
mParameters = mCamera!!.parameters
|
||||
mMaxZoom = mParameters!!.maxZoom
|
||||
mZoomRatios = mParameters!!.zoomRatios
|
||||
mSupportedPreviewSizes = mParameters!!.supportedPreviewSizes
|
||||
Collections.sort<Camera.Size>(mSupportedPreviewSizes!!, SizesComparator())
|
||||
requestLayout()
|
||||
invalidate()
|
||||
mSetupPreviewAfterMeasure = true
|
||||
|
||||
val focusModes = mParameters!!.supportedFocusModes
|
||||
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
|
||||
mParameters!!.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
|
||||
|
||||
val rotation = Utils.getPreviewRotation(mActivity, cameraId)
|
||||
mCamera!!.setDisplayOrientation(rotation)
|
||||
mCamera!!.parameters = mParameters
|
||||
|
||||
if (mCanTakePicture) {
|
||||
try {
|
||||
mCamera!!.setPreviewDisplay(mSurfaceHolder)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "setCamera setPreviewDisplay " + e.message)
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mCallback.setFlashAvailable(Utils.hasFlash(mCamera))
|
||||
}
|
||||
|
||||
if (mIsVideoMode) {
|
||||
initRecorder()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun setTargetUri(uri: Uri) {
|
||||
mTargetUri = uri
|
||||
}
|
||||
|
||||
private fun initGestureDetector() {
|
||||
mScaleGestureDetector = ScaleGestureDetector(mActivity, object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||
val zoomFactor = mParameters!!.zoom
|
||||
var zoomRatio = mZoomRatios!![zoomFactor] / 100f
|
||||
zoomRatio *= detector.scaleFactor
|
||||
|
||||
var newZoomFactor = zoomFactor
|
||||
if (zoomRatio <= 1f) {
|
||||
newZoomFactor = 0
|
||||
} else if (zoomRatio >= mZoomRatios!![mMaxZoom] / 100f) {
|
||||
newZoomFactor = mMaxZoom
|
||||
} else {
|
||||
if (detector.scaleFactor > 1f) {
|
||||
for (i in zoomFactor..mZoomRatios!!.size - 1) {
|
||||
if (mZoomRatios!![i] / 100.0f >= zoomRatio) {
|
||||
newZoomFactor = i
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i in zoomFactor downTo 0) {
|
||||
if (mZoomRatios!![i] / 100.0f <= zoomRatio) {
|
||||
newZoomFactor = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newZoomFactor = Math.max(newZoomFactor, 0)
|
||||
newZoomFactor = Math.min(mMaxZoom, newZoomFactor)
|
||||
|
||||
mParameters!!.zoom = newZoomFactor
|
||||
if (mCamera != null)
|
||||
mCamera!!.parameters = mParameters
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onScaleEnd(detector: ScaleGestureDetector) {
|
||||
super.onScaleEnd(detector)
|
||||
mWasZooming = true
|
||||
mSurfaceView.isSoundEffectsEnabled = false
|
||||
mParameters!!.focusAreas = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun takePicture() {
|
||||
if (mCanTakePicture) {
|
||||
var rotation = Utils.getMediaRotation(mActivity, mCurrCameraId)
|
||||
rotation += Utils.compensateDeviceRotation(mCurrCameraId, mCallback.getCurrentOrientation())
|
||||
|
||||
/*final Camera.Size maxSize = getOptimalPictureSize();
|
||||
mParameters.setPictureSize(maxSize.width, maxSize.height);*/
|
||||
mParameters!!.setRotation(rotation % 360)
|
||||
|
||||
if (mConfig!!.isSoundEnabled) {
|
||||
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
val volume = audioManager.getStreamVolume(AudioManager.STREAM_SYSTEM)
|
||||
if (volume != 0) {
|
||||
val mp = MediaPlayer.create(context, Uri.parse("file:///system/media/audio/ui/camera_click.ogg"))
|
||||
mp?.start()
|
||||
}
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
mCamera!!.enableShutterSound(false)
|
||||
}
|
||||
|
||||
mCamera!!.parameters = mParameters
|
||||
mCamera!!.takePicture(null, null, takePictureCallback)
|
||||
}
|
||||
mCanTakePicture = false
|
||||
}
|
||||
|
||||
private val takePictureCallback = Camera.PictureCallback { data, cam ->
|
||||
if (mConfig!!.isShowPreviewEnabled) {
|
||||
Handler().postDelayed({ resumePreview() }, PHOTO_PREVIEW_LENGTH.toLong())
|
||||
} else {
|
||||
resumePreview()
|
||||
}
|
||||
|
||||
PhotoProcessor(mActivity, mTargetUri).execute(data)
|
||||
}
|
||||
|
||||
private fun resumePreview() {
|
||||
if (mCamera != null) {
|
||||
mCamera!!.startPreview()
|
||||
}
|
||||
|
||||
mCanTakePicture = true
|
||||
}
|
||||
|
||||
val supportedVideoSizes: List<Camera.Size>
|
||||
get() {
|
||||
if (mParameters!!.supportedVideoSizes != null) {
|
||||
return mParameters!!.supportedVideoSizes
|
||||
} else {
|
||||
return mParameters!!.supportedPreviewSizes
|
||||
}
|
||||
}
|
||||
|
||||
private fun focusArea(takePictureAfter: Boolean) {
|
||||
if (mCamera == null)
|
||||
return
|
||||
|
||||
mCamera!!.cancelAutoFocus()
|
||||
val focusRect = calculateFocusArea(mLastClickX.toFloat(), mLastClickY.toFloat())
|
||||
if (mParameters!!.maxNumFocusAreas > 0) {
|
||||
val focusAreas = ArrayList<Camera.Area>(1)
|
||||
focusAreas.add(Camera.Area(focusRect, 1000))
|
||||
mParameters!!.focusAreas = focusAreas
|
||||
mCallback.drawFocusRect(mLastClickX, mLastClickY)
|
||||
}
|
||||
|
||||
mCamera!!.parameters = mParameters
|
||||
mCamera!!.autoFocus { success, camera ->
|
||||
camera.cancelAutoFocus()
|
||||
val focusModes = mParameters!!.supportedFocusModes
|
||||
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
|
||||
mParameters!!.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
|
||||
|
||||
camera.parameters = mParameters
|
||||
if (takePictureAfter) {
|
||||
takePicture()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateFocusArea(x: Float, y: Float): Rect {
|
||||
var left = java.lang.Float.valueOf(x / mSurfaceView.width * 2000 - 1000)!!.toInt()
|
||||
var top = java.lang.Float.valueOf(y / mSurfaceView.height * 2000 - 1000)!!.toInt()
|
||||
|
||||
val tmp = left
|
||||
left = top
|
||||
top = -tmp
|
||||
|
||||
val rectLeft = Math.max(left - FOCUS_AREA_SIZE / 2, -1000)
|
||||
val rectTop = Math.max(top - FOCUS_AREA_SIZE / 2, -1000)
|
||||
val rectRight = Math.min(left + FOCUS_AREA_SIZE / 2, 1000)
|
||||
val rectBottom = Math.min(top + FOCUS_AREA_SIZE / 2, 1000)
|
||||
return Rect(rectLeft, rectTop, rectRight, rectBottom)
|
||||
}
|
||||
|
||||
fun releaseCamera() {
|
||||
stopRecording()
|
||||
|
||||
if (mCamera != null) {
|
||||
mCamera!!.stopPreview()
|
||||
mCamera!!.release()
|
||||
mCamera = null
|
||||
}
|
||||
|
||||
cleanupRecorder()
|
||||
}
|
||||
|
||||
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||
mIsSurfaceCreated = true
|
||||
try {
|
||||
if (mCamera != null) {
|
||||
mCamera!!.setPreviewDisplay(mSurfaceHolder)
|
||||
}
|
||||
|
||||
if (mSwitchToVideoAsap)
|
||||
initRecorder()
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "surfaceCreated IOException " + e.message)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
|
||||
mIsSurfaceCreated = true
|
||||
|
||||
if (mIsVideoMode) {
|
||||
initRecorder()
|
||||
}
|
||||
}
|
||||
|
||||
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||
mIsSurfaceCreated = false
|
||||
if (mCamera != null) {
|
||||
mCamera!!.stopPreview()
|
||||
}
|
||||
|
||||
cleanupRecorder()
|
||||
}
|
||||
|
||||
private fun setupPreview() {
|
||||
mCanTakePicture = true
|
||||
if (mCamera != null && mPreviewSize != null) {
|
||||
mParameters!!.setPreviewSize(mPreviewSize!!.width, mPreviewSize!!.height)
|
||||
mCamera!!.parameters = mParameters
|
||||
mCamera!!.startPreview()
|
||||
}
|
||||
}
|
||||
|
||||
private fun cleanupRecorder() {
|
||||
if (mRecorder != null) {
|
||||
if (mIsRecording) {
|
||||
stopRecording()
|
||||
}
|
||||
|
||||
mRecorder!!.release()
|
||||
mRecorder = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
||||
|
||||
}
|
||||
|
||||
private fun getOptimalPreviewSize(sizes: List<Camera.Size>, width: Int, height: Int): Camera.Size {
|
||||
var result: Camera.Size? = null
|
||||
for (size in sizes) {
|
||||
if (size.width <= width && size.height <= height) {
|
||||
if (result == null) {
|
||||
result = size
|
||||
} else {
|
||||
val resultArea = result.width * result.height
|
||||
val newArea = size.width * size.height
|
||||
|
||||
if (newArea > resultArea) {
|
||||
result = size
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result!!
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
setMeasuredDimension(mScreenSize.x, mScreenSize.y)
|
||||
|
||||
if (mSupportedPreviewSizes != null) {
|
||||
// for simplicity lets assume that most displays are 16:9 and the remaining ones are 4:3
|
||||
// always set 16:9 for videos as many devices support 4:3 only in low quality
|
||||
if (mForceAspectRatio || mIsVideoMode) {
|
||||
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes!!, mScreenSize.y, mScreenSize.x)
|
||||
} else {
|
||||
val newRatioHeight = (mScreenSize.x * (4.toDouble() / 3)).toInt()
|
||||
setMeasuredDimension(mScreenSize.x, newRatioHeight)
|
||||
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes!!, newRatioHeight, mScreenSize.x)
|
||||
}
|
||||
val lp = mSurfaceView.layoutParams
|
||||
|
||||
// make sure to occupy whole width in every case
|
||||
if (mScreenSize.x > mPreviewSize!!.height) {
|
||||
val ratio = mScreenSize.x.toFloat() / mPreviewSize!!.height
|
||||
lp.width = (mPreviewSize!!.height * ratio).toInt()
|
||||
if (mForceAspectRatio || mIsVideoMode) {
|
||||
lp.height = mScreenSize.y
|
||||
} else {
|
||||
lp.height = (mPreviewSize!!.width * ratio).toInt()
|
||||
}
|
||||
} else {
|
||||
lp.width = mPreviewSize!!.height
|
||||
lp.height = mPreviewSize!!.width
|
||||
}
|
||||
|
||||
if (mSetupPreviewAfterMeasure) {
|
||||
mSetupPreviewAfterMeasure = false
|
||||
if (mCamera != null)
|
||||
mCamera!!.stopPreview()
|
||||
|
||||
setupPreview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||
mLastClickX = event.x.toInt()
|
||||
mLastClickY = event.y.toInt()
|
||||
|
||||
if (mMaxZoom > 0)
|
||||
mScaleGestureDetector!!.onTouchEvent(event)
|
||||
return false
|
||||
}
|
||||
|
||||
fun enableFlash() {
|
||||
mParameters!!.flashMode = Camera.Parameters.FLASH_MODE_TORCH
|
||||
mCamera!!.parameters = mParameters
|
||||
mIsFlashEnabled = true
|
||||
}
|
||||
|
||||
fun disableFlash() {
|
||||
mIsFlashEnabled = false
|
||||
mParameters!!.flashMode = Camera.Parameters.FLASH_MODE_OFF
|
||||
mCamera!!.parameters = mParameters
|
||||
}
|
||||
|
||||
fun initPhotoMode() {
|
||||
stopRecording()
|
||||
cleanupRecorder()
|
||||
mIsRecording = false
|
||||
mIsVideoMode = false
|
||||
recheckAspectRatio()
|
||||
}
|
||||
|
||||
private fun recheckAspectRatio() {
|
||||
if (!mForceAspectRatio) {
|
||||
mSetupPreviewAfterMeasure = true
|
||||
invalidate()
|
||||
requestLayout()
|
||||
}
|
||||
}
|
||||
|
||||
// VIDEO RECORDING
|
||||
fun initRecorder(): Boolean {
|
||||
if (mCamera == null || mRecorder != null || !mIsSurfaceCreated)
|
||||
return false
|
||||
|
||||
mSwitchToVideoAsap = false
|
||||
val previewSizes = mParameters!!.supportedPreviewSizes
|
||||
Collections.sort<Camera.Size>(previewSizes, SizesComparator())
|
||||
val preferred = previewSizes[0]
|
||||
|
||||
mParameters!!.setPreviewSize(preferred.width, preferred.height)
|
||||
mCamera!!.parameters = mParameters
|
||||
|
||||
mIsRecording = false
|
||||
mIsVideoMode = true
|
||||
recheckAspectRatio()
|
||||
mRecorder = MediaRecorder()
|
||||
mRecorder!!.setCamera(mCamera)
|
||||
mRecorder!!.setVideoSource(MediaRecorder.VideoSource.DEFAULT)
|
||||
mRecorder!!.setAudioSource(MediaRecorder.AudioSource.DEFAULT)
|
||||
|
||||
mCurrVideoPath = Utils.getOutputMediaFile(mActivity, false)
|
||||
if (mCurrVideoPath!!.isEmpty()) {
|
||||
mActivity.toast(R.string.video_creating_error)
|
||||
return false
|
||||
}
|
||||
|
||||
/*final Camera.Size videoSize = getOptimalVideoSize();
|
||||
final CamcorderProfile cpHigh = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
|
||||
cpHigh.videoFrameWidth = videoSize.width;
|
||||
cpHigh.videoFrameHeight = videoSize.height;
|
||||
mRecorder.setProfile(cpHigh);*/
|
||||
|
||||
if (Utils.needsStupidWritePermissions(context, mCurrVideoPath!!)) {
|
||||
if (mConfig!!.treeUri.isEmpty()) {
|
||||
mActivity.toast(R.string.save_error_internal_storage)
|
||||
mConfig!!.savePhotosFolder = Environment.getExternalStorageDirectory().toString()
|
||||
releaseCamera()
|
||||
return false
|
||||
}
|
||||
try {
|
||||
/*var document: DocumentFile = Utils.getFileDocument(context, mCurrVideoPath!!, mConfig!!.treeUri)
|
||||
document = document.createFile("", mCurrVideoPath!!.substring(mCurrVideoPath!!.lastIndexOf('/') + 1))
|
||||
val uri = document.uri
|
||||
val fileDescriptor = context.contentResolver.openFileDescriptor(uri, "rw")
|
||||
mRecorder!!.setOutputFile(fileDescriptor!!.fileDescriptor)*/
|
||||
} catch (e: Exception) {
|
||||
setupFailed(e)
|
||||
}
|
||||
|
||||
} else {
|
||||
mRecorder!!.setOutputFile(mCurrVideoPath)
|
||||
}
|
||||
mRecorder!!.setPreviewDisplay(mSurfaceHolder.surface)
|
||||
|
||||
val rotation = Utils.getFinalRotation(mActivity, mCurrCameraId, mCallback.getCurrentOrientation())
|
||||
mInitVideoRotation = rotation
|
||||
mRecorder!!.setOrientationHint(rotation)
|
||||
|
||||
try {
|
||||
mRecorder!!.prepare()
|
||||
} catch (e: Exception) {
|
||||
setupFailed(e)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun setupFailed(e: Exception) {
|
||||
mActivity.toast(R.string.video_setup_error)
|
||||
Log.e(TAG, "initRecorder " + e.message)
|
||||
releaseCamera()
|
||||
}
|
||||
|
||||
fun toggleRecording(): Boolean {
|
||||
if (mIsRecording) {
|
||||
stopRecording()
|
||||
initRecorder()
|
||||
} else {
|
||||
startRecording()
|
||||
}
|
||||
return mIsRecording
|
||||
}
|
||||
|
||||
private fun startRecording() {
|
||||
if (mInitVideoRotation != Utils.getFinalRotation(mActivity, mCurrCameraId, mCallback.getCurrentOrientation())) {
|
||||
cleanupRecorder()
|
||||
initRecorder()
|
||||
}
|
||||
|
||||
try {
|
||||
mCamera!!.unlock()
|
||||
toggleShutterSound(true)
|
||||
mRecorder!!.start()
|
||||
toggleShutterSound(false)
|
||||
mIsRecording = true
|
||||
} catch (e: Exception) {
|
||||
mActivity.toast(R.string.video_setup_error)
|
||||
Log.e(TAG, "toggleRecording " + e.message)
|
||||
releaseCamera()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun stopRecording() {
|
||||
if (mRecorder != null && mIsRecording) {
|
||||
try {
|
||||
toggleShutterSound(true)
|
||||
mRecorder!!.stop()
|
||||
val paths = arrayListOf<String>(mCurrVideoPath!!)
|
||||
mActivity.scanPaths(paths) {}
|
||||
} catch (e: RuntimeException) {
|
||||
toggleShutterSound(false)
|
||||
File(mCurrVideoPath!!).delete()
|
||||
mActivity.toast(R.string.video_saving_error)
|
||||
Log.e(TAG, "stopRecording " + e.message)
|
||||
mRecorder = null
|
||||
mIsRecording = false
|
||||
releaseCamera()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mRecorder = null
|
||||
mIsRecording = false
|
||||
|
||||
val file = File(mCurrVideoPath!!)
|
||||
if (file.exists() && file.length() == 0L) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleShutterSound(mute: Boolean?) {
|
||||
if (!mConfig!!.isSoundEnabled) {
|
||||
(mActivity.getSystemService(Context.AUDIO_SERVICE) as AudioManager).setStreamMute(AudioManager.STREAM_SYSTEM, mute!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
if (!mWasZooming)
|
||||
focusArea(false)
|
||||
|
||||
mWasZooming = false
|
||||
mSurfaceView.isSoundEffectsEnabled = true
|
||||
}
|
||||
|
||||
override fun onScanCompleted(path: String, uri: Uri) {
|
||||
mCallback.videoSaved(uri)
|
||||
toggleShutterSound(false)
|
||||
}
|
||||
|
||||
private class SizesComparator : Comparator<Camera.Size>, Serializable {
|
||||
|
||||
override fun compare(a: Camera.Size, b: Camera.Size): Int {
|
||||
return b.width * b.height - a.width * a.height
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = 5431278455314658485L
|
||||
}
|
||||
}
|
||||
|
||||
interface PreviewListener {
|
||||
fun setFlashAvailable(available: Boolean)
|
||||
|
||||
fun setIsCameraAvailable(available: Boolean)
|
||||
|
||||
fun getCurrentOrientation(): Int
|
||||
|
||||
fun videoSaved(uri: Uri)
|
||||
|
||||
fun drawFocusRect(x: Int, y: Int)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue