Browse Source

源码依赖 jcameraView

imndx 5 months ago
parent
commit
34468c71c3
47 changed files with 3725 additions and 1 deletions
  1. 1 0
      cameraview/.gitignore
  2. 35 0
      cameraview/build.gradle
  3. 0 0
      cameraview/consumer-rules.pro
  4. 21 0
      cameraview/proguard-rules.pro
  5. 26 0
      cameraview/src/androidTest/java/cn/wildfirechat/cameraview/ExampleInstrumentedTest.java
  6. 7 0
      cameraview/src/main/AndroidManifest.xml
  7. 780 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/CameraInterface.java
  8. 368 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/CaptureButton.java
  9. 367 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/CaptureLayout.java
  10. 65 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/FoucsView.java
  11. 600 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/JCameraView.java
  12. 63 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/ReturnButton.java
  13. 109 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/TypeButton.java
  14. 20 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/listener/CaptureListener.java
  15. 13 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/listener/ClickListener.java
  16. 14 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/listener/ErrorListener.java
  17. 19 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/listener/JCameraListener.java
  18. 13 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/listener/ResultListener.java
  19. 13 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/listener/ReturnListener.java
  20. 15 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/listener/TypeListener.java
  21. 89 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/state/BorrowPictureState.java
  22. 89 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/state/BorrowVideoState.java
  23. 133 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/state/CameraMachine.java
  24. 109 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/state/PreviewState.java
  25. 41 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/state/State.java
  26. 52 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/util/AngleUtil.java
  27. 24 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/util/AudioUtil.java
  28. 158 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/util/CameraParamUtil.java
  29. 104 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/util/CheckPermission.java
  30. 46 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/util/DeviceUtil.java
  31. 70 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/util/FileUtil.java
  32. 56 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/util/LogUtil.java
  33. 29 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/util/ScreenUtils.java
  34. 29 0
      cameraview/src/main/java/com/cjt2325/cameralibrary/view/CameraView.java
  35. 9 0
      cameraview/src/main/res/drawable/ic_camera.xml
  36. 9 0
      cameraview/src/main/res/drawable/ic_flash_auto.xml
  37. 9 0
      cameraview/src/main/res/drawable/ic_flash_off.xml
  38. 9 0
      cameraview/src/main/res/drawable/ic_flash_on.xml
  39. 9 0
      cameraview/src/main/res/drawable/ic_photo.xml
  40. 61 0
      cameraview/src/main/res/layout/camera_view.xml
  41. 17 0
      cameraview/src/main/res/values/attrs.xml
  42. 3 0
      cameraview/src/main/res/values/strings.xml
  43. 17 0
      cameraview/src/test/java/cn/wildfirechat/cameraview/ExampleUnitTest.java
  44. 1 0
      settings.gradle
  45. 1 1
      uikit-aar-dep/build.gradle
  46. BIN
      uikit-aar-dep/com.cjt2325.cameralibrary-library-1.1.9.aar
  47. 2 0
      uikit/build.gradle

+ 1 - 0
cameraview/.gitignore

@@ -0,0 +1 @@
+/build

+ 35 - 0
cameraview/build.gradle

@@ -0,0 +1,35 @@
+plugins {
+    id 'com.android.library'
+}
+
+android {
+    namespace 'com.cjt2325.cameralibrary'
+    compileSdk 34
+
+    defaultConfig {
+        minSdk 24
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles "consumer-rules.pro"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_11
+        targetCompatibility JavaVersion.VERSION_11
+    }
+}
+
+dependencies {
+
+    implementation 'androidx.appcompat:appcompat:1.7.0'
+    implementation 'com.google.android.material:material:1.12.0'
+    testImplementation 'junit:junit:4.13.2'
+    androidTestImplementation 'androidx.test.ext:junit:1.2.1'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
+}

+ 0 - 0
cameraview/consumer-rules.pro


+ 21 - 0
cameraview/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 26 - 0
cameraview/src/androidTest/java/cn/wildfirechat/cameraview/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package cn.wildfirechat.cameraview;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertEquals("cn.wildfirechat.cameraview.test", appContext.getPackageName());
+    }
+}

+ 7 - 0
cameraview/src/main/AndroidManifest.xml

@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <application
+        android:allowBackup="true"
+        android:label="@string/app_name"
+        android:supportsRtl="true"></application>
+</manifest>

+ 780 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/CameraInterface.java

@@ -0,0 +1,780 @@
+package com.cjt2325.cameralibrary;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ImageFormat;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.YuvImage;
+import android.hardware.Camera;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.media.MediaRecorder;
+import android.os.Build;
+import android.os.Environment;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.widget.ImageView;
+
+import com.cjt2325.cameralibrary.listener.ErrorListener;
+import com.cjt2325.cameralibrary.util.AngleUtil;
+import com.cjt2325.cameralibrary.util.CameraParamUtil;
+import com.cjt2325.cameralibrary.util.CheckPermission;
+import com.cjt2325.cameralibrary.util.DeviceUtil;
+import com.cjt2325.cameralibrary.util.FileUtil;
+import com.cjt2325.cameralibrary.util.LogUtil;
+import com.cjt2325.cameralibrary.util.ScreenUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.graphics.Bitmap.createBitmap;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/4/25
+ * 描    述:camera操作单例
+ * =====================================
+ */
+@SuppressWarnings("deprecation")
+public class CameraInterface implements Camera.PreviewCallback {
+
+    private static final String TAG = "CJT";
+
+    private volatile static CameraInterface mCameraInterface;
+
+    public static void destroyCameraInterface() {
+        if (mCameraInterface != null) {
+            mCameraInterface = null;
+        }
+    }
+
+    private Camera mCamera;
+    private Camera.Parameters mParams;
+    private boolean isPreviewing = false;
+
+    private int SELECTED_CAMERA = -1;
+    private int CAMERA_POST_POSITION = -1;
+    private int CAMERA_FRONT_POSITION = -1;
+
+    private SurfaceHolder mHolder = null;
+    private float screenProp = -1.0f;
+
+    private boolean isRecorder = false;
+    private MediaRecorder mediaRecorder;
+    private String videoFileName;
+    private String saveVideoPath;
+    private String videoFileAbsPath;
+    private Bitmap videoFirstFrame = null;
+
+    private ErrorListener errorLisenter;
+
+    private ImageView mSwitchView;
+    private ImageView mFlashLamp;
+
+    private int preview_width;
+    private int preview_height;
+
+    private int angle = 0;
+    private int cameraAngle = 90;//摄像头角度   默认为90度
+    private int rotation = 0;
+    private byte[] firstFrame_data;
+
+    public static final int TYPE_RECORDER = 0x090;
+    public static final int TYPE_CAPTURE = 0x091;
+    private int nowScaleRate = 0;
+    private int recordScleRate = 0;
+
+    //视频质量
+    private int mediaQuality = JCameraView.MEDIA_QUALITY_MIDDLE;
+    private SensorManager sm = null;
+
+    //获取CameraInterface单例
+    public static synchronized CameraInterface getInstance() {
+        if (mCameraInterface == null)
+            synchronized (CameraInterface.class) {
+                if (mCameraInterface == null)
+                    mCameraInterface = new CameraInterface();
+            }
+        return mCameraInterface;
+    }
+
+    public void setSwitchView(ImageView mSwitchView, ImageView mFlashLamp) {
+        this.mSwitchView = mSwitchView;
+        this.mFlashLamp = mFlashLamp;
+        if (mSwitchView != null) {
+            cameraAngle = CameraParamUtil.getInstance().getCameraDisplayOrientation(mSwitchView.getContext(),
+                    SELECTED_CAMERA);
+        }
+    }
+
+    private SensorEventListener sensorEventListener = new SensorEventListener() {
+        public void onSensorChanged(SensorEvent event) {
+            if (Sensor.TYPE_ACCELEROMETER != event.sensor.getType()) {
+                return;
+            }
+            float[] values = event.values;
+            angle = AngleUtil.getSensorAngle(values[0], values[1]);
+            rotationAnimation();
+        }
+
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        }
+    };
+
+    //切换摄像头icon跟随手机角度进行旋转
+    private void rotationAnimation() {
+        if (mSwitchView == null) {
+            return;
+        }
+        if (rotation != angle) {
+            int start_rotaion = 0;
+            int end_rotation = 0;
+            switch (rotation) {
+                case 0:
+                    start_rotaion = 0;
+                    switch (angle) {
+                        case 90:
+                            end_rotation = -90;
+                            break;
+                        case 270:
+                            end_rotation = 90;
+                            break;
+                    }
+                    break;
+                case 90:
+                    start_rotaion = -90;
+                    switch (angle) {
+                        case 0:
+                            end_rotation = 0;
+                            break;
+                        case 180:
+                            end_rotation = -180;
+                            break;
+                    }
+                    break;
+                case 180:
+                    start_rotaion = 180;
+                    switch (angle) {
+                        case 90:
+                            end_rotation = 270;
+                            break;
+                        case 270:
+                            end_rotation = 90;
+                            break;
+                    }
+                    break;
+                case 270:
+                    start_rotaion = 90;
+                    switch (angle) {
+                        case 0:
+                            end_rotation = 0;
+                            break;
+                        case 180:
+                            end_rotation = 180;
+                            break;
+                    }
+                    break;
+            }
+            ObjectAnimator animC = ObjectAnimator.ofFloat(mSwitchView, "rotation", start_rotaion, end_rotation);
+            ObjectAnimator animF = ObjectAnimator.ofFloat(mFlashLamp, "rotation", start_rotaion, end_rotation);
+            AnimatorSet set = new AnimatorSet();
+            set.playTogether(animC, animF);
+            set.setDuration(500);
+            set.start();
+            rotation = angle;
+        }
+    }
+
+    @SuppressWarnings("ResultOfMethodCallIgnored")
+    void setSaveVideoPath(String saveVideoPath) {
+        this.saveVideoPath = saveVideoPath;
+        File file = new File(saveVideoPath);
+        if (!file.exists()) {
+            file.mkdirs();
+        }
+    }
+
+
+    public void setZoom(float zoom, int type) {
+        if (mCamera == null) {
+            return;
+        }
+        if (mParams == null) {
+            mParams = mCamera.getParameters();
+        }
+        if (!mParams.isZoomSupported() || !mParams.isSmoothZoomSupported()) {
+            return;
+        }
+        switch (type) {
+            case TYPE_RECORDER:
+                //如果不是录制视频中,上滑不会缩放
+                if (!isRecorder) {
+                    return;
+                }
+                if (zoom >= 0) {
+                    //每移动50个像素缩放一个级别
+                    int scaleRate = (int) (zoom / 40);
+                    if (scaleRate <= mParams.getMaxZoom() && scaleRate >= nowScaleRate && recordScleRate != scaleRate) {
+                        mParams.setZoom(scaleRate);
+                        mCamera.setParameters(mParams);
+                        recordScleRate = scaleRate;
+                    }
+                }
+                break;
+            case TYPE_CAPTURE:
+                if (isRecorder) {
+                    return;
+                }
+                //每移动50个像素缩放一个级别
+                int scaleRate = (int) (zoom / 50);
+                if (scaleRate < mParams.getMaxZoom()) {
+                    nowScaleRate += scaleRate;
+                    if (nowScaleRate < 0) {
+                        nowScaleRate = 0;
+                    } else if (nowScaleRate > mParams.getMaxZoom()) {
+                        nowScaleRate = mParams.getMaxZoom();
+                    }
+                    mParams.setZoom(nowScaleRate);
+                    mCamera.setParameters(mParams);
+                }
+                LogUtil.i("setZoom = " + nowScaleRate);
+                break;
+        }
+
+    }
+
+    void setMediaQuality(int quality) {
+        this.mediaQuality = quality;
+    }
+
+
+    @Override
+    public void onPreviewFrame(byte[] data, Camera camera) {
+        firstFrame_data = data;
+    }
+
+    public void setFlashMode(String flashMode) {
+        if (mCamera == null)
+            return;
+        Camera.Parameters params = mCamera.getParameters();
+        params.setFlashMode(flashMode);
+        mCamera.setParameters(params);
+    }
+
+
+    public interface CameraOpenOverCallback {
+        void cameraHasOpened();
+    }
+
+    private CameraInterface() {
+        findAvailableCameras();
+        SELECTED_CAMERA = CAMERA_POST_POSITION;
+        saveVideoPath = "";
+    }
+
+
+    /**
+     * open Camera
+     */
+    void doOpenCamera(CameraOpenOverCallback callback) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            if (!CheckPermission.isCameraUseable(SELECTED_CAMERA) && this.errorLisenter != null) {
+                this.errorLisenter.onError();
+                return;
+            }
+        }
+        if (mCamera == null) {
+            openCamera(SELECTED_CAMERA);
+        }
+        callback.cameraHasOpened();
+    }
+
+    private void setFlashModel() {
+        mParams = mCamera.getParameters();
+        mParams.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); //设置camera参数为Torch模式
+        mCamera.setParameters(mParams);
+    }
+
+    private synchronized void openCamera(int id) {
+        try {
+            this.mCamera = Camera.open(id);
+        } catch (Exception var3) {
+            var3.printStackTrace();
+            if (this.errorLisenter != null) {
+                this.errorLisenter.onError();
+            }
+        }
+
+        if (Build.VERSION.SDK_INT > 17 && this.mCamera != null) {
+            try {
+                this.mCamera.enableShutterSound(false);
+            } catch (Exception e) {
+                e.printStackTrace();
+                Log.e("CJT", "enable shutter sound faild");
+            }
+        }
+    }
+
+    public synchronized void switchCamera(SurfaceHolder holder, float screenProp) {
+        if (SELECTED_CAMERA == CAMERA_POST_POSITION) {
+            SELECTED_CAMERA = CAMERA_FRONT_POSITION;
+        } else {
+            SELECTED_CAMERA = CAMERA_POST_POSITION;
+        }
+        doDestroyCamera();
+        LogUtil.i("open start");
+        openCamera(SELECTED_CAMERA);
+//        mCamera = Camera.open();
+        if (Build.VERSION.SDK_INT > 17 && this.mCamera != null) {
+            try {
+                this.mCamera.enableShutterSound(false);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        LogUtil.i("open end");
+        doStartPreview(holder, screenProp);
+    }
+
+    /**
+     * doStartPreview
+     */
+    public void doStartPreview(SurfaceHolder holder, float screenProp) {
+        if (isPreviewing) {
+            LogUtil.i("doStartPreview isPreviewing");
+        }
+        if (this.screenProp < 0) {
+            this.screenProp = screenProp;
+        }
+        if (holder == null) {
+            return;
+        }
+        this.mHolder = holder;
+        if (mCamera != null) {
+            try {
+                mParams = mCamera.getParameters();
+                Camera.Size previewSize = CameraParamUtil.getInstance().getPreviewSize(mParams
+                        .getSupportedPreviewSizes(), 1000, screenProp);
+                Camera.Size pictureSize = CameraParamUtil.getInstance().getPictureSize(mParams
+                        .getSupportedPictureSizes(), 1200, screenProp);
+
+                mParams.setPreviewSize(previewSize.width, previewSize.height);
+
+                preview_width = previewSize.width;
+                preview_height = previewSize.height;
+
+                mParams.setPictureSize(pictureSize.width, pictureSize.height);
+
+                if (CameraParamUtil.getInstance().isSupportedFocusMode(
+                        mParams.getSupportedFocusModes(),
+                        Camera.Parameters.FOCUS_MODE_AUTO)) {
+                    mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
+                }
+                if (CameraParamUtil.getInstance().isSupportedPictureFormats(mParams.getSupportedPictureFormats(),
+                        ImageFormat.JPEG)) {
+                    mParams.setPictureFormat(ImageFormat.JPEG);
+                    mParams.setJpegQuality(100);
+                }
+                mCamera.setParameters(mParams);
+                mParams = mCamera.getParameters();
+                mCamera.setPreviewDisplay(holder);  //SurfaceView
+                mCamera.setDisplayOrientation(cameraAngle);//浏览角度
+                mCamera.setPreviewCallback(this); //每一帧回调
+                mCamera.startPreview();//启动浏览
+                isPreviewing = true;
+                Log.i(TAG, "=== Start Preview ===");
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * 停止预览
+     */
+    public void doStopPreview() {
+        if (null != mCamera) {
+            try {
+                mCamera.setPreviewCallback(null);
+                mCamera.stopPreview();
+                //这句要在stopPreview后执行,不然会卡顿或者花屏
+                mCamera.setPreviewDisplay(null);
+                isPreviewing = false;
+                Log.i(TAG, "=== Stop Preview ===");
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * 销毁Camera
+     */
+    void doDestroyCamera() {
+        errorLisenter = null;
+        if (null != mCamera) {
+            try {
+                mCamera.setPreviewCallback(null);
+                mSwitchView = null;
+                mFlashLamp = null;
+                mCamera.stopPreview();
+                //这句要在stopPreview后执行,不然会卡顿或者花屏
+                mCamera.setPreviewDisplay(null);
+                mHolder = null;
+                isPreviewing = false;
+                mCamera.release();
+                mCamera = null;
+//                destroyCameraInterface();
+                Log.i(TAG, "=== Destroy Camera ===");
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } else {
+            Log.i(TAG, "=== Camera  Null===");
+        }
+    }
+
+    /**
+     * 拍照
+     */
+    private int nowAngle;
+
+    public void takePicture(final TakePictureCallback callback) {
+        if (mCamera == null) {
+            return;
+        }
+        switch (cameraAngle) {
+            case 90:
+                nowAngle = Math.abs(angle + cameraAngle) % 360;
+                break;
+            case 270:
+                nowAngle = Math.abs(cameraAngle - angle);
+                break;
+        }
+//
+        Log.i("CJT", angle + " = " + cameraAngle + " = " + nowAngle);
+        mCamera.takePicture(null, null, new Camera.PictureCallback() {
+            @Override
+            public void onPictureTaken(byte[] data, Camera camera) {
+                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
+                Matrix matrix = new Matrix();
+                if (SELECTED_CAMERA == CAMERA_POST_POSITION) {
+                    matrix.setRotate(nowAngle);
+                } else if (SELECTED_CAMERA == CAMERA_FRONT_POSITION) {
+                    matrix.setRotate(360 - nowAngle);
+                    matrix.postScale(-1, 1);
+                }
+
+                bitmap = createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+                if (callback != null) {
+                    if (nowAngle == 90 || nowAngle == 270) {
+                        callback.captureResult(bitmap, true);
+                    } else {
+                        callback.captureResult(bitmap, false);
+                    }
+                }
+            }
+        });
+    }
+
+    //启动录像
+    public void startRecord(Surface surface, float screenProp, ErrorCallback callback) {
+        mCamera.setPreviewCallback(null);
+        final int nowAngle = (angle + 90) % 360;
+        //获取第一帧图片
+        Camera.Parameters parameters = mCamera.getParameters();
+        int width = parameters.getPreviewSize().width;
+        int height = parameters.getPreviewSize().height;
+        YuvImage yuv = new YuvImage(firstFrame_data, parameters.getPreviewFormat(), width, height, null);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        yuv.compressToJpeg(new Rect(0, 0, width, height), 50, out);
+        byte[] bytes = out.toByteArray();
+        videoFirstFrame = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+        Matrix matrix = new Matrix();
+        if (SELECTED_CAMERA == CAMERA_POST_POSITION) {
+            matrix.setRotate(nowAngle);
+        } else if (SELECTED_CAMERA == CAMERA_FRONT_POSITION) {
+            matrix.setRotate(270);
+        }
+        videoFirstFrame = createBitmap(videoFirstFrame, 0, 0, videoFirstFrame.getWidth(), videoFirstFrame
+                .getHeight(), matrix, true);
+
+        if (isRecorder) {
+            return;
+        }
+        if (mCamera == null) {
+            openCamera(SELECTED_CAMERA);
+        }
+        if (mediaRecorder == null) {
+            mediaRecorder = new MediaRecorder();
+        }
+        if (mParams == null) {
+            mParams = mCamera.getParameters();
+        }
+        List<String> focusModes = mParams.getSupportedFocusModes();
+        if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
+            mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
+        }
+        mCamera.setParameters(mParams);
+        mCamera.unlock();
+        mediaRecorder.reset();
+        mediaRecorder.setCamera(mCamera);
+        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+
+        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+
+        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+
+
+        Camera.Size videoSize;
+        if (mParams.getSupportedVideoSizes() == null) {
+            videoSize = CameraParamUtil.getInstance().getPreviewSize(mParams.getSupportedPreviewSizes(), 600,
+                    screenProp);
+        } else {
+            videoSize = CameraParamUtil.getInstance().getPreviewSize(mParams.getSupportedVideoSizes(), 600,
+                    screenProp);
+        }
+        Log.i(TAG, "setVideoSize    width = " + videoSize.width + "height = " + videoSize.height);
+        if (videoSize.width == videoSize.height) {
+            mediaRecorder.setVideoSize(preview_width, preview_height);
+        } else {
+            mediaRecorder.setVideoSize(videoSize.width, videoSize.height);
+        }
+//        if (SELECTED_CAMERA == CAMERA_FRONT_POSITION) {
+//            mediaRecorder.setOrientationHint(270);
+//        } else {
+//            mediaRecorder.setOrientationHint(nowAngle);
+////            mediaRecorder.setOrientationHint(90);
+//        }
+
+        if (SELECTED_CAMERA == CAMERA_FRONT_POSITION) {
+            //手机预览倒立的处理
+            if (cameraAngle == 270) {
+                //横屏
+                if (nowAngle == 0) {
+                    mediaRecorder.setOrientationHint(180);
+                } else if (nowAngle == 270) {
+                    mediaRecorder.setOrientationHint(270);
+                } else {
+                    mediaRecorder.setOrientationHint(90);
+                }
+            } else {
+                if (nowAngle == 90) {
+                    mediaRecorder.setOrientationHint(270);
+                } else if (nowAngle == 270) {
+                    mediaRecorder.setOrientationHint(90);
+                } else {
+                    mediaRecorder.setOrientationHint(nowAngle);
+                }
+            }
+        } else {
+            mediaRecorder.setOrientationHint(nowAngle);
+        }
+
+
+        if (DeviceUtil.isHuaWeiRongyao()) {
+            mediaRecorder.setVideoEncodingBitRate(4 * 100000);
+        } else {
+            mediaRecorder.setVideoEncodingBitRate(mediaQuality);
+        }
+        mediaRecorder.setPreviewDisplay(surface);
+
+        videoFileName = "video_" + System.currentTimeMillis() + ".mp4";
+        if (saveVideoPath.equals("")) {
+            saveVideoPath = Environment.getExternalStorageDirectory().getPath();
+        }
+        videoFileAbsPath = saveVideoPath + File.separator + videoFileName;
+        mediaRecorder.setOutputFile(videoFileAbsPath);
+        try {
+            mediaRecorder.prepare();
+            mediaRecorder.start();
+            isRecorder = true;
+        } catch (IllegalStateException e) {
+            e.printStackTrace();
+            Log.i("CJT", "startRecord IllegalStateException");
+            if (this.errorLisenter != null) {
+                this.errorLisenter.onError();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            Log.i("CJT", "startRecord IOException");
+            if (this.errorLisenter != null) {
+                this.errorLisenter.onError();
+            }
+        } catch (RuntimeException e) {
+            Log.i("CJT", "startRecord RuntimeException");
+        }
+    }
+
+    //停止录像
+    public void stopRecord(boolean isShort, StopRecordCallback callback) {
+        if (!isRecorder) {
+            return;
+        }
+        if (mediaRecorder != null) {
+            mediaRecorder.setOnErrorListener(null);
+            mediaRecorder.setOnInfoListener(null);
+            mediaRecorder.setPreviewDisplay(null);
+            try {
+                mediaRecorder.stop();
+            } catch (RuntimeException e) {
+                e.printStackTrace();
+                mediaRecorder = null;
+                mediaRecorder = new MediaRecorder();
+            } finally {
+                if (mediaRecorder != null) {
+                    mediaRecorder.release();
+                }
+                mediaRecorder = null;
+                isRecorder = false;
+            }
+            if (isShort) {
+                if (FileUtil.deleteFile(videoFileAbsPath)) {
+                    callback.recordResult(null, null);
+                }
+                return;
+            }
+            doStopPreview();
+            String fileName = saveVideoPath + File.separator + videoFileName;
+            callback.recordResult(fileName, videoFirstFrame);
+        }
+    }
+
+    private void findAvailableCameras() {
+        Camera.CameraInfo info = new Camera.CameraInfo();
+        int cameraNum = Camera.getNumberOfCameras();
+        for (int i = 0; i < cameraNum; i++) {
+            Camera.getCameraInfo(i, info);
+            switch (info.facing) {
+                case Camera.CameraInfo.CAMERA_FACING_FRONT:
+                    CAMERA_FRONT_POSITION = info.facing;
+                    break;
+                case Camera.CameraInfo.CAMERA_FACING_BACK:
+                    CAMERA_POST_POSITION = info.facing;
+                    break;
+            }
+        }
+    }
+
+    int handlerTime = 0;
+
+    public void handleFocus(final Context context, final float x, final float y, final FocusCallback callback) {
+        if (mCamera == null) {
+            return;
+        }
+        final Camera.Parameters params = mCamera.getParameters();
+        Rect focusRect = calculateTapArea(x, y, 1f, context);
+        mCamera.cancelAutoFocus();
+        if (params.getMaxNumFocusAreas() > 0) {
+            List<Camera.Area> focusAreas = new ArrayList<>();
+            focusAreas.add(new Camera.Area(focusRect, 800));
+            params.setFocusAreas(focusAreas);
+        } else {
+            Log.i(TAG, "focus areas not supported");
+            callback.focusSuccess();
+            return;
+        }
+        final String currentFocusMode = params.getFocusMode();
+        try {
+            params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
+            mCamera.setParameters(params);
+            mCamera.autoFocus(new Camera.AutoFocusCallback() {
+                @Override
+                public void onAutoFocus(boolean success, Camera camera) {
+                    if (success || handlerTime > 10) {
+                        Camera.Parameters params = camera.getParameters();
+                        params.setFocusMode(currentFocusMode);
+                        camera.setParameters(params);
+                        handlerTime = 0;
+                        callback.focusSuccess();
+                    } else {
+                        handlerTime++;
+                        handleFocus(context, x, y, callback);
+                    }
+                }
+            });
+        } catch (Exception e) {
+            Log.e(TAG, "autoFocus failer");
+        }
+    }
+
+
+    private static Rect calculateTapArea(float x, float y, float coefficient, Context context) {
+        float focusAreaSize = 300;
+        int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue();
+        int centerX = (int) (x / ScreenUtils.getScreenWidth(context) * 2000 - 1000);
+        int centerY = (int) (y / ScreenUtils.getScreenHeight(context) * 2000 - 1000);
+        int left = clamp(centerX - areaSize / 2, -1000, 1000);
+        int top = clamp(centerY - areaSize / 2, -1000, 1000);
+        RectF rectF = new RectF(left, top, left + areaSize, top + areaSize);
+        return new Rect(Math.round(rectF.left), Math.round(rectF.top), Math.round(rectF.right), Math.round(rectF
+                .bottom));
+    }
+
+    private static int clamp(int x, int min, int max) {
+        if (x > max) {
+            return max;
+        }
+        if (x < min) {
+            return min;
+        }
+        return x;
+    }
+
+    void setErrorLinsenter(ErrorListener errorLisenter) {
+        this.errorLisenter = errorLisenter;
+    }
+
+
+    public interface StopRecordCallback {
+        void recordResult(String url, Bitmap firstFrame);
+    }
+
+    interface ErrorCallback {
+        void onError();
+    }
+
+    public interface TakePictureCallback {
+        void captureResult(Bitmap bitmap, boolean isVertical);
+    }
+
+    public interface FocusCallback {
+        void focusSuccess();
+
+    }
+
+
+    void registerSensorManager(Context context) {
+        if (sm == null) {
+            sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+        }
+        sm.registerListener(sensorEventListener, sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager
+                .SENSOR_DELAY_NORMAL);
+    }
+
+    void unregisterSensorManager(Context context) {
+        if (sm == null) {
+            sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+        }
+        sm.unregisterListener(sensorEventListener);
+    }
+
+    void isPreview(boolean res) {
+        this.isPreviewing = res;
+    }
+}

+ 368 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/CaptureButton.java

@@ -0,0 +1,368 @@
+package com.cjt2325.cameralibrary;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.os.CountDownTimer;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.cjt2325.cameralibrary.listener.CaptureListener;
+import com.cjt2325.cameralibrary.util.CheckPermission;
+import com.cjt2325.cameralibrary.util.LogUtil;
+
+import static com.cjt2325.cameralibrary.JCameraView.BUTTON_STATE_BOTH;
+import static com.cjt2325.cameralibrary.JCameraView.BUTTON_STATE_ONLY_CAPTURE;
+import static com.cjt2325.cameralibrary.JCameraView.BUTTON_STATE_ONLY_RECORDER;
+
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐 445263848@qq.com
+ * 版    本:1.1.4
+ * 创建日期:2017/4/25
+ * 描    述:拍照按钮
+ * =====================================
+ */
+public class CaptureButton extends View {
+
+    private int state;              //当前按钮状态
+    private int button_state;       //按钮可执行的功能状态(拍照,录制,两者)
+
+    public static final int STATE_IDLE = 0x001;        //空闲状态
+    public static final int STATE_PRESS = 0x002;       //按下状态
+    public static final int STATE_LONG_PRESS = 0x003;  //长按状态
+    public static final int STATE_RECORDERING = 0x004; //录制状态
+    public static final int STATE_BAN = 0x005;         //禁止状态
+
+    private int progress_color = 0xEE16AE16;            //进度条颜色
+    private int outside_color = 0xEEDCDCDC;             //外圆背景色
+    private int inside_color = 0xFFFFFFFF;              //内圆背景色
+
+
+    private float event_Y;  //Touch_Event_Down时候记录的Y值
+
+
+    private Paint mPaint;
+
+    private float strokeWidth;          //进度条宽度
+    private int outside_add_size;       //长按外圆半径变大的Size
+    private int inside_reduce_size;     //长安内圆缩小的Size
+
+    //中心坐标
+    private float center_X;
+    private float center_Y;
+
+    private float button_radius;            //按钮半径
+    private float button_outside_radius;    //外圆半径
+    private float button_inside_radius;     //内圆半径
+    private int button_size;                //按钮大小
+
+    private float progress;         //录制视频的进度
+    private int duration;           //录制视频最大时间长度
+    private int min_duration;       //最短录制时间限制
+    private int recorded_time;      //记录当前录制的时间
+
+    private RectF rectF;
+
+    private LongPressRunnable longPressRunnable;    //长按后处理的逻辑Runnable
+    private CaptureListener captureLisenter;        //按钮回调接口
+    private RecordCountDownTimer timer;             //计时器
+
+    public CaptureButton(Context context) {
+        super(context);
+    }
+
+    public CaptureButton(Context context, int size) {
+        super(context);
+        this.button_size = size;
+        button_radius = size / 2.0f;
+
+        button_outside_radius = button_radius;
+        button_inside_radius = button_radius * 0.75f;
+
+        strokeWidth = size / 15;
+        outside_add_size = size / 5;
+        inside_reduce_size = size / 8;
+
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+
+        progress = 0;
+        longPressRunnable = new LongPressRunnable();
+
+        state = STATE_IDLE;                //初始化为空闲状态
+        button_state = BUTTON_STATE_BOTH;  //初始化按钮为可录制可拍照
+        LogUtil.i("CaptureButtom start");
+        duration = 10 * 1000;              //默认最长录制时间为10s
+        LogUtil.i("CaptureButtom end");
+        min_duration = 1500;              //默认最短录制时间为1.5s
+
+        center_X = (button_size + outside_add_size * 2) / 2;
+        center_Y = (button_size + outside_add_size * 2) / 2;
+
+        rectF = new RectF(
+                center_X - (button_radius + outside_add_size - strokeWidth / 2),
+                center_Y - (button_radius + outside_add_size - strokeWidth / 2),
+                center_X + (button_radius + outside_add_size - strokeWidth / 2),
+                center_Y + (button_radius + outside_add_size - strokeWidth / 2));
+
+        timer = new RecordCountDownTimer(duration, duration / 360);    //录制定时器
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(button_size + outside_add_size * 2, button_size + outside_add_size * 2);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        mPaint.setStyle(Paint.Style.FILL);
+
+        mPaint.setColor(outside_color); //外圆(半透明灰色)
+        canvas.drawCircle(center_X, center_Y, button_outside_radius, mPaint);
+
+        mPaint.setColor(inside_color);  //内圆(白色)
+        canvas.drawCircle(center_X, center_Y, button_inside_radius, mPaint);
+
+        //如果状态为录制状态,则绘制录制进度条
+        if (state == STATE_RECORDERING) {
+            mPaint.setColor(progress_color);
+            mPaint.setStyle(Paint.Style.STROKE);
+            mPaint.setStrokeWidth(strokeWidth);
+            canvas.drawArc(rectF, -90, progress, false, mPaint);
+        }
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                LogUtil.i("state = " + state);
+                if (event.getPointerCount() > 1 || state != STATE_IDLE)
+                    break;
+                event_Y = event.getY();     //记录Y值
+                state = STATE_PRESS;        //修改当前状态为点击按下
+
+                //判断按钮状态是否为可录制状态
+                if ((button_state == BUTTON_STATE_ONLY_RECORDER || button_state == BUTTON_STATE_BOTH))
+                    postDelayed(longPressRunnable, 500);    //同时延长500启动长按后处理的逻辑Runnable
+                break;
+            case MotionEvent.ACTION_MOVE:
+                if (captureLisenter != null
+                        && state == STATE_RECORDERING
+                        && (button_state == BUTTON_STATE_ONLY_RECORDER || button_state == BUTTON_STATE_BOTH)) {
+                    //记录当前Y值与按下时候Y值的差值,调用缩放回调接口
+                    captureLisenter.recordZoom(event_Y - event.getY());
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                //根据当前按钮的状态进行相应的处理
+                handlerUnpressByState();
+                break;
+        }
+        return true;
+    }
+
+    //当手指松开按钮时候处理的逻辑
+    private void handlerUnpressByState() {
+        removeCallbacks(longPressRunnable); //移除长按逻辑的Runnable
+        //根据当前状态处理
+        switch (state) {
+            //当前是点击按下
+            case STATE_PRESS:
+                if (captureLisenter != null && (button_state == BUTTON_STATE_ONLY_CAPTURE || button_state ==
+                        BUTTON_STATE_BOTH)) {
+                    startCaptureAnimation(button_inside_radius);
+                } else {
+                    state = STATE_IDLE;
+                }
+                break;
+            //当前是长按状态
+            case STATE_RECORDERING:
+                timer.cancel(); //停止计时器
+                recordEnd();    //录制结束
+                break;
+        }
+    }
+
+    //录制结束
+    private void recordEnd() {
+        if (captureLisenter != null) {
+            if (recorded_time < min_duration)
+                captureLisenter.recordShort(recorded_time);//回调录制时间过短
+            else
+                captureLisenter.recordEnd(recorded_time);  //回调录制结束
+        }
+        resetRecordAnim();  //重制按钮状态
+    }
+
+    //重制状态
+    private void resetRecordAnim() {
+        state = STATE_BAN;
+        progress = 0;       //重制进度
+        invalidate();
+        //还原按钮初始状态动画
+        startRecordAnimation(
+                button_outside_radius,
+                button_radius,
+                button_inside_radius,
+                button_radius * 0.75f
+        );
+    }
+
+    //内圆动画
+    private void startCaptureAnimation(float inside_start) {
+        ValueAnimator inside_anim = ValueAnimator.ofFloat(inside_start, inside_start * 0.75f, inside_start);
+        inside_anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                button_inside_radius = (float) animation.getAnimatedValue();
+                invalidate();
+            }
+        });
+        inside_anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                //回调拍照接口
+                captureLisenter.takePictures();
+                state = STATE_BAN;
+            }
+        });
+        inside_anim.setDuration(100);
+        inside_anim.start();
+    }
+
+    //内外圆动画
+    private void startRecordAnimation(float outside_start, float outside_end, float inside_start, float inside_end) {
+        ValueAnimator outside_anim = ValueAnimator.ofFloat(outside_start, outside_end);
+        ValueAnimator inside_anim = ValueAnimator.ofFloat(inside_start, inside_end);
+        //外圆动画监听
+        outside_anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                button_outside_radius = (float) animation.getAnimatedValue();
+                invalidate();
+            }
+        });
+        //内圆动画监听
+        inside_anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                button_inside_radius = (float) animation.getAnimatedValue();
+                invalidate();
+            }
+        });
+        AnimatorSet set = new AnimatorSet();
+        //当动画结束后启动录像Runnable并且回调录像开始接口
+        set.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                //设置为录制状态
+                if (state == STATE_LONG_PRESS) {
+                    if (captureLisenter != null)
+                        captureLisenter.recordStart();
+                    state = STATE_RECORDERING;
+                    timer.start();
+                }
+            }
+        });
+        set.playTogether(outside_anim, inside_anim);
+        set.setDuration(100);
+        set.start();
+    }
+
+
+    //更新进度条
+    private void updateProgress(long millisUntilFinished) {
+        recorded_time = (int) (duration - millisUntilFinished);
+        progress = 360f - millisUntilFinished / (float) duration * 360f;
+        invalidate();
+    }
+
+    //录制视频计时器
+    private class RecordCountDownTimer extends CountDownTimer {
+        RecordCountDownTimer(long millisInFuture, long countDownInterval) {
+            super(millisInFuture, countDownInterval);
+        }
+
+        @Override
+        public void onTick(long millisUntilFinished) {
+            updateProgress(millisUntilFinished);
+        }
+
+        @Override
+        public void onFinish() {
+            updateProgress(0);
+            recordEnd();
+        }
+    }
+
+    //长按线程
+    private class LongPressRunnable implements Runnable {
+        @Override
+        public void run() {
+            state = STATE_LONG_PRESS;   //如果按下后经过500毫秒则会修改当前状态为长按状态
+            //没有录制权限
+            if (CheckPermission.getRecordState() != CheckPermission.STATE_SUCCESS) {
+                state = STATE_IDLE;
+                if (captureLisenter != null) {
+                    captureLisenter.recordError();
+                    return;
+                }
+            }
+            //启动按钮动画,外圆变大,内圆缩小
+            startRecordAnimation(
+                    button_outside_radius,
+                    button_outside_radius + outside_add_size,
+                    button_inside_radius,
+                    button_inside_radius - inside_reduce_size
+            );
+        }
+    }
+
+    /**************************************************
+     * 对外提供的API                     *
+     **************************************************/
+
+    //设置最长录制时间
+    public void setDuration(int duration) {
+        this.duration = duration;
+        timer = new RecordCountDownTimer(duration, duration / 360);    //录制定时器
+    }
+
+    //设置最短录制时间
+    public void setMinDuration(int duration) {
+        this.min_duration = duration;
+    }
+
+    //设置回调接口
+    public void setCaptureLisenter(CaptureListener captureLisenter) {
+        this.captureLisenter = captureLisenter;
+    }
+
+    //设置按钮功能(拍照和录像)
+    public void setButtonFeatures(int state) {
+        this.button_state = state;
+    }
+
+    //是否空闲状态
+    public boolean isIdle() {
+        return state == STATE_IDLE ? true : false;
+    }
+
+    //设置状态
+    public void resetState() {
+        state = STATE_IDLE;
+    }
+}

+ 367 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/CaptureLayout.java

@@ -0,0 +1,367 @@
+package com.cjt2325.cameralibrary;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.cjt2325.cameralibrary.listener.CaptureListener;
+import com.cjt2325.cameralibrary.listener.ClickListener;
+import com.cjt2325.cameralibrary.listener.ReturnListener;
+import com.cjt2325.cameralibrary.listener.TypeListener;
+
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐 445263848@qq.com
+ * 版    本:1.0.4
+ * 创建日期:2017/4/26
+ * 描    述:集成各个控件的布局
+ * =====================================
+ */
+
+public class CaptureLayout extends FrameLayout {
+
+    private CaptureListener captureLisenter;    //拍照按钮监听
+    private TypeListener typeLisenter;          //拍照或录制后接结果按钮监听
+    private ReturnListener returnListener;      //退出按钮监听
+    private ClickListener leftClickListener;    //左边按钮监听
+    private ClickListener rightClickListener;   //右边按钮监听
+
+    public void setTypeLisenter(TypeListener typeLisenter) {
+        this.typeLisenter = typeLisenter;
+    }
+
+    public void setCaptureLisenter(CaptureListener captureLisenter) {
+        this.captureLisenter = captureLisenter;
+    }
+
+    public void setReturnLisenter(ReturnListener returnListener) {
+        this.returnListener = returnListener;
+    }
+
+    private CaptureButton btn_capture;      //拍照按钮
+    private TypeButton btn_confirm;         //确认按钮
+    private TypeButton btn_cancel;          //取消按钮
+    private ReturnButton btn_return;        //返回按钮
+    private ImageView iv_custom_left;            //左边自定义按钮
+    private ImageView iv_custom_right;            //右边自定义按钮
+    private TextView txt_tip;               //提示文本
+
+    private int layout_width;
+    private int layout_height;
+    private int button_size;
+    private int iconLeft = 0;
+    private int iconRight = 0;
+
+    private boolean isFirst = true;
+
+    public CaptureLayout(Context context) {
+        this(context, null);
+    }
+
+    public CaptureLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CaptureLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        DisplayMetrics outMetrics = new DisplayMetrics();
+        manager.getDefaultDisplay().getMetrics(outMetrics);
+
+        if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+            layout_width = outMetrics.widthPixels;
+        } else {
+            layout_width = outMetrics.widthPixels / 2;
+        }
+        button_size = (int) (layout_width / 4.5f);
+        layout_height = button_size + (button_size / 5) * 2 + 100;
+
+        initView();
+        initEvent();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(layout_width, layout_height);
+    }
+
+    public void initEvent() {
+        //默认Typebutton为隐藏
+        iv_custom_right.setVisibility(GONE);
+        btn_cancel.setVisibility(GONE);
+        btn_confirm.setVisibility(GONE);
+    }
+
+    public void startTypeBtnAnimator() {
+        //拍照录制结果后的动画
+        if (this.iconLeft != 0)
+            iv_custom_left.setVisibility(GONE);
+        else
+            btn_return.setVisibility(GONE);
+        if (this.iconRight != 0)
+            iv_custom_right.setVisibility(GONE);
+        btn_capture.setVisibility(GONE);
+        btn_cancel.setVisibility(VISIBLE);
+        btn_confirm.setVisibility(VISIBLE);
+        btn_cancel.setClickable(false);
+        btn_confirm.setClickable(false);
+        ObjectAnimator animator_cancel = ObjectAnimator.ofFloat(btn_cancel, "translationX", layout_width / 4, 0);
+        ObjectAnimator animator_confirm = ObjectAnimator.ofFloat(btn_confirm, "translationX", -layout_width / 4, 0);
+
+        AnimatorSet set = new AnimatorSet();
+        set.playTogether(animator_cancel, animator_confirm);
+        set.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                btn_cancel.setClickable(true);
+                btn_confirm.setClickable(true);
+            }
+        });
+        set.setDuration(200);
+        set.start();
+    }
+
+
+    private void initView() {
+        setWillNotDraw(false);
+        //拍照按钮
+        btn_capture = new CaptureButton(getContext(), button_size);
+        LayoutParams btn_capture_param = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+        btn_capture_param.gravity = Gravity.CENTER;
+        btn_capture.setLayoutParams(btn_capture_param);
+        btn_capture.setCaptureLisenter(new CaptureListener() {
+            @Override
+            public void takePictures() {
+                if (captureLisenter != null) {
+                    captureLisenter.takePictures();
+                }
+            }
+
+            @Override
+            public void recordShort(long time) {
+                if (captureLisenter != null) {
+                    captureLisenter.recordShort(time);
+                }
+                startAlphaAnimation();
+            }
+
+            @Override
+            public void recordStart() {
+                if (captureLisenter != null) {
+                    captureLisenter.recordStart();
+                }
+                startAlphaAnimation();
+            }
+
+            @Override
+            public void recordEnd(long time) {
+                if (captureLisenter != null) {
+                    captureLisenter.recordEnd(time);
+                }
+                startAlphaAnimation();
+                startTypeBtnAnimator();
+            }
+
+            @Override
+            public void recordZoom(float zoom) {
+                if (captureLisenter != null) {
+                    captureLisenter.recordZoom(zoom);
+                }
+            }
+
+            @Override
+            public void recordError() {
+                if (captureLisenter != null) {
+                    captureLisenter.recordError();
+                }
+            }
+        });
+
+        //取消按钮
+        btn_cancel = new TypeButton(getContext(), TypeButton.TYPE_CANCEL, button_size);
+        final LayoutParams btn_cancel_param = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+        btn_cancel_param.gravity = Gravity.CENTER_VERTICAL;
+        btn_cancel_param.setMargins((layout_width / 4) - button_size / 2, 0, 0, 0);
+        btn_cancel.setLayoutParams(btn_cancel_param);
+        btn_cancel.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                if (typeLisenter != null) {
+                    typeLisenter.cancel();
+                }
+                startAlphaAnimation();
+//                resetCaptureLayout();
+            }
+        });
+
+        //确认按钮
+        btn_confirm = new TypeButton(getContext(), TypeButton.TYPE_CONFIRM, button_size);
+        LayoutParams btn_confirm_param = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+        btn_confirm_param.gravity = Gravity.CENTER_VERTICAL | Gravity.RIGHT;
+        btn_confirm_param.setMargins(0, 0, (layout_width / 4) - button_size / 2, 0);
+        btn_confirm.setLayoutParams(btn_confirm_param);
+        btn_confirm.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                if (typeLisenter != null) {
+                    typeLisenter.confirm();
+                }
+                startAlphaAnimation();
+//                resetCaptureLayout();
+            }
+        });
+
+        //返回按钮
+        btn_return = new ReturnButton(getContext(), (int) (button_size / 2.5f));
+        LayoutParams btn_return_param = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+        btn_return_param.gravity = Gravity.CENTER_VERTICAL;
+        btn_return_param.setMargins(layout_width / 6, 0, 0, 0);
+        btn_return.setLayoutParams(btn_return_param);
+        btn_return.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (leftClickListener != null) {
+                    leftClickListener.onClick();
+                }
+            }
+        });
+        //左边自定义按钮
+        iv_custom_left = new ImageView(getContext());
+        LayoutParams iv_custom_param_left = new LayoutParams((int) (button_size / 2.5f), (int) (button_size / 2.5f));
+        iv_custom_param_left.gravity = Gravity.CENTER_VERTICAL;
+        iv_custom_param_left.setMargins(layout_width / 6, 0, 0, 0);
+        iv_custom_left.setLayoutParams(iv_custom_param_left);
+        iv_custom_left.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (leftClickListener != null) {
+                    leftClickListener.onClick();
+                }
+            }
+        });
+
+        //右边自定义按钮
+        iv_custom_right = new ImageView(getContext());
+        LayoutParams iv_custom_param_right = new LayoutParams((int) (button_size / 2.5f), (int) (button_size / 2.5f));
+        iv_custom_param_right.gravity = Gravity.CENTER_VERTICAL | Gravity.RIGHT;
+        iv_custom_param_right.setMargins(0, 0, layout_width / 6, 0);
+        iv_custom_right.setLayoutParams(iv_custom_param_right);
+        iv_custom_right.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (rightClickListener != null) {
+                    rightClickListener.onClick();
+                }
+            }
+        });
+
+        txt_tip = new TextView(getContext());
+        LayoutParams txt_param = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+        txt_param.gravity = Gravity.CENTER_HORIZONTAL;
+        txt_param.setMargins(0, 0, 0, 0);
+        txt_tip.setText("轻触拍照,长按摄像");
+        txt_tip.setTextColor(0xFFFFFFFF);
+        txt_tip.setGravity(Gravity.CENTER);
+        txt_tip.setLayoutParams(txt_param);
+
+        this.addView(btn_capture);
+        this.addView(btn_cancel);
+        this.addView(btn_confirm);
+        this.addView(btn_return);
+        this.addView(iv_custom_left);
+        this.addView(iv_custom_right);
+        this.addView(txt_tip);
+
+    }
+
+    /**************************************************
+     * 对外提供的API                      *
+     **************************************************/
+    public void resetCaptureLayout() {
+        btn_capture.resetState();
+        btn_cancel.setVisibility(GONE);
+        btn_confirm.setVisibility(GONE);
+        btn_capture.setVisibility(VISIBLE);
+        if (this.iconLeft != 0)
+            iv_custom_left.setVisibility(VISIBLE);
+        else
+            btn_return.setVisibility(VISIBLE);
+        if (this.iconRight != 0)
+            iv_custom_right.setVisibility(VISIBLE);
+    }
+
+
+    public void startAlphaAnimation() {
+        if (isFirst) {
+            ObjectAnimator animator_txt_tip = ObjectAnimator.ofFloat(txt_tip, "alpha", 1f, 0f);
+            animator_txt_tip.setDuration(500);
+            animator_txt_tip.start();
+            isFirst = false;
+        }
+    }
+
+    public void setTextWithAnimation(String tip) {
+        txt_tip.setText(tip);
+        ObjectAnimator animator_txt_tip = ObjectAnimator.ofFloat(txt_tip, "alpha", 0f, 1f, 1f, 0f);
+        animator_txt_tip.setDuration(2500);
+        animator_txt_tip.start();
+    }
+
+    public void setDuration(int duration) {
+        btn_capture.setDuration(duration);
+    }
+
+    public void setButtonFeatures(int state) {
+        btn_capture.setButtonFeatures(state);
+    }
+
+    public void setTip(String tip) {
+        txt_tip.setText(tip);
+    }
+
+    public void showTip() {
+        txt_tip.setVisibility(VISIBLE);
+    }
+
+    public void setIconSrc(int iconLeft, int iconRight) {
+        this.iconLeft = iconLeft;
+        this.iconRight = iconRight;
+        if (this.iconLeft != 0) {
+            iv_custom_left.setImageResource(iconLeft);
+            iv_custom_left.setVisibility(VISIBLE);
+            btn_return.setVisibility(GONE);
+        } else {
+            iv_custom_left.setVisibility(GONE);
+            btn_return.setVisibility(VISIBLE);
+        }
+        if (this.iconRight != 0) {
+            iv_custom_right.setImageResource(iconRight);
+            iv_custom_right.setVisibility(VISIBLE);
+        } else {
+            iv_custom_right.setVisibility(GONE);
+        }
+    }
+
+    public void setLeftClickListener(ClickListener leftClickListener) {
+        this.leftClickListener = leftClickListener;
+    }
+
+    public void setRightClickListener(ClickListener rightClickListener) {
+        this.rightClickListener = rightClickListener;
+    }
+}

+ 65 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/FoucsView.java

@@ -0,0 +1,65 @@
+package com.cjt2325.cameralibrary;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.cjt2325.cameralibrary.util.ScreenUtils;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/4/26
+ * 描    述:对焦框
+ * =====================================
+ */
+public class FoucsView extends View {
+    private int size;
+    private int center_x;
+    private int center_y;
+    private int length;
+    private Paint mPaint;
+
+    public FoucsView(Context context) {
+        this(context, null);
+    }
+
+    public FoucsView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FoucsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        this.size = ScreenUtils.getScreenWidth(context) / 3;
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+        mPaint.setDither(true);
+        mPaint.setColor(0xEE16AE16);
+        mPaint.setStrokeWidth(4);
+        mPaint.setStyle(Paint.Style.STROKE);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        center_x = (int) (size / 2.0);
+        center_y = (int) (size / 2.0);
+        length = (int) (size / 2.0) - 2;
+        setMeasuredDimension(size, size);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        canvas.drawRect(center_x - length, center_y - length, center_x + length, center_y + length, mPaint);
+        canvas.drawLine(2, getHeight() / 2, size / 10, getHeight() / 2, mPaint);
+        canvas.drawLine(getWidth() - 2, getHeight() / 2, getWidth() - size / 10, getHeight() / 2, mPaint);
+        canvas.drawLine(getWidth() / 2, 2, getWidth() / 2, size / 10, mPaint);
+        canvas.drawLine(getWidth() / 2, getHeight() - 2, getWidth() / 2, getHeight() - size / 10, mPaint);
+    }
+}

+ 600 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/JCameraView.java

@@ -0,0 +1,600 @@
+package com.cjt2325.cameralibrary;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.hardware.Camera;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.VideoView;
+
+import androidx.annotation.RequiresApi;
+
+import com.cjt2325.cameralibrary.listener.CaptureListener;
+import com.cjt2325.cameralibrary.listener.ClickListener;
+import com.cjt2325.cameralibrary.listener.ErrorListener;
+import com.cjt2325.cameralibrary.listener.JCameraListener;
+import com.cjt2325.cameralibrary.listener.TypeListener;
+import com.cjt2325.cameralibrary.state.CameraMachine;
+import com.cjt2325.cameralibrary.util.FileUtil;
+import com.cjt2325.cameralibrary.util.LogUtil;
+import com.cjt2325.cameralibrary.util.ScreenUtils;
+import com.cjt2325.cameralibrary.view.CameraView;
+
+import java.io.IOException;
+
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.0.4
+ * 创建日期:2017/4/25
+ * 描    述:
+ * =====================================
+ */
+public class JCameraView extends FrameLayout implements CameraInterface.CameraOpenOverCallback, SurfaceHolder
+        .Callback, CameraView {
+//    private static final String TAG = "JCameraView";
+
+    //Camera状态机
+    private CameraMachine machine;
+
+    //闪关灯状态
+    private static final int TYPE_FLASH_AUTO = 0x021;
+    private static final int TYPE_FLASH_ON = 0x022;
+    private static final int TYPE_FLASH_OFF = 0x023;
+    private int type_flash = TYPE_FLASH_OFF;
+
+    //拍照浏览时候的类型
+    public static final int TYPE_PICTURE = 0x001;
+    public static final int TYPE_VIDEO = 0x002;
+    public static final int TYPE_SHORT = 0x003;
+    public static final int TYPE_DEFAULT = 0x004;
+
+    //录制视频比特率
+    public static final int MEDIA_QUALITY_HIGH = 20 * 100000;
+    public static final int MEDIA_QUALITY_MIDDLE = 16 * 100000;
+    public static final int MEDIA_QUALITY_LOW = 12 * 100000;
+    public static final int MEDIA_QUALITY_POOR = 8 * 100000;
+    public static final int MEDIA_QUALITY_FUNNY = 4 * 100000;
+    public static final int MEDIA_QUALITY_DESPAIR = 2 * 100000;
+    public static final int MEDIA_QUALITY_SORRY = 1 * 80000;
+
+
+    public static final int BUTTON_STATE_ONLY_CAPTURE = 0x101;      //只能拍照
+    public static final int BUTTON_STATE_ONLY_RECORDER = 0x102;     //只能录像
+    public static final int BUTTON_STATE_BOTH = 0x103;              //两者都可以
+
+
+    //回调监听
+    private JCameraListener jCameraLisenter;
+    private ClickListener leftClickListener;
+    private ClickListener rightClickListener;
+
+    private Context mContext;
+    private VideoView mVideoView;
+    private ImageView mPhoto;
+    private ImageView mSwitchCamera;
+    private ImageView mFlashLamp;
+    private CaptureLayout mCaptureLayout;
+    private FoucsView mFoucsView;
+    private MediaPlayer mMediaPlayer;
+
+    private int layout_width;
+    private float screenProp = 0f;
+
+    private Bitmap captureBitmap;   //捕获的图片
+    private Bitmap firstFrame;      //第一帧图片
+    private String videoUrl;        //视频URL
+
+
+    //切换摄像头按钮的参数
+    private int iconSize = 0;       //图标大小
+    private int iconMargin = 0;     //右上边距
+    private int iconSrc = 0;        //图标资源
+    private int iconLeft = 0;       //左图标
+    private int iconRight = 0;      //右图标
+    private int duration = 0;       //录制时间
+
+    //缩放梯度
+    private int zoomGradient = 0;
+
+    private boolean firstTouch = true;
+    private float firstTouchLength = 0;
+
+    public JCameraView(Context context) {
+        this(context, null);
+    }
+
+    public JCameraView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public JCameraView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mContext = context;
+        //get AttributeSet
+        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.JCameraView, defStyleAttr, 0);
+        iconSize = a.getDimensionPixelSize(R.styleable.JCameraView_iconSize, (int) TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_SP, 35, getResources().getDisplayMetrics()));
+        iconMargin = a.getDimensionPixelSize(R.styleable.JCameraView_iconMargin, (int) TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_SP, 15, getResources().getDisplayMetrics()));
+        iconSrc = a.getResourceId(R.styleable.JCameraView_iconSrc, R.drawable.ic_camera);
+        iconLeft = a.getResourceId(R.styleable.JCameraView_iconLeft, 0);
+        iconRight = a.getResourceId(R.styleable.JCameraView_iconRight, 0);
+        duration = a.getInteger(R.styleable.JCameraView_duration_max, 10 * 1000);       //没设置默认为10s
+        a.recycle();
+        initData();
+        initView();
+    }
+
+    private void initData() {
+        layout_width = ScreenUtils.getScreenWidth(mContext);
+        //缩放梯度
+        zoomGradient = (int) (layout_width / 16f);
+        LogUtil.i("zoom = " + zoomGradient);
+        machine = new CameraMachine(getContext(), this, this);
+    }
+
+    private void initView() {
+        setWillNotDraw(false);
+        View view = LayoutInflater.from(mContext).inflate(R.layout.camera_view, this);
+        mVideoView = (VideoView) view.findViewById(R.id.video_preview);
+        mPhoto = (ImageView) view.findViewById(R.id.image_photo);
+        mSwitchCamera = (ImageView) view.findViewById(R.id.image_switch);
+        mSwitchCamera.setImageResource(iconSrc);
+        mFlashLamp = (ImageView) view.findViewById(R.id.image_flash);
+        setFlashRes();
+        mFlashLamp.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                type_flash++;
+                if (type_flash > 0x023)
+                    type_flash = TYPE_FLASH_AUTO;
+                setFlashRes();
+            }
+        });
+        mCaptureLayout = (CaptureLayout) view.findViewById(R.id.capture_layout);
+        mCaptureLayout.setDuration(duration);
+        mCaptureLayout.setIconSrc(iconLeft, iconRight);
+        mFoucsView = (FoucsView) view.findViewById(R.id.fouce_view);
+        mVideoView.getHolder().addCallback(this);
+        //切换摄像头
+        mSwitchCamera.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                machine.swtich(mVideoView.getHolder(), screenProp);
+            }
+        });
+        //拍照 录像
+        mCaptureLayout.setCaptureLisenter(new CaptureListener() {
+            @Override
+            public void takePictures() {
+                mSwitchCamera.setVisibility(INVISIBLE);
+                mFlashLamp.setVisibility(INVISIBLE);
+                machine.capture();
+            }
+
+            @Override
+            public void recordStart() {
+                mSwitchCamera.setVisibility(INVISIBLE);
+                mFlashLamp.setVisibility(INVISIBLE);
+                machine.record(mVideoView.getHolder().getSurface(), screenProp);
+            }
+
+            @Override
+            public void recordShort(final long time) {
+                mCaptureLayout.setTextWithAnimation("录制时间过短");
+                mSwitchCamera.setVisibility(VISIBLE);
+                mFlashLamp.setVisibility(VISIBLE);
+                postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        machine.stopRecord(true, time);
+                    }
+                }, 1500 - time);
+            }
+
+            @Override
+            public void recordEnd(long time) {
+                machine.stopRecord(false, time);
+            }
+
+            @Override
+            public void recordZoom(float zoom) {
+                LogUtil.i("recordZoom");
+                machine.zoom(zoom, CameraInterface.TYPE_RECORDER);
+            }
+
+            @Override
+            public void recordError() {
+                if (errorLisenter != null) {
+                    errorLisenter.AudioPermissionError();
+                }
+            }
+        });
+        //确认 取消
+        mCaptureLayout.setTypeLisenter(new TypeListener() {
+            @Override
+            public void cancel() {
+                machine.cancle(mVideoView.getHolder(), screenProp);
+            }
+
+            @Override
+            public void confirm() {
+                machine.confirm();
+            }
+        });
+        //退出
+//        mCaptureLayout.setReturnLisenter(new ReturnListener() {
+//            @Override
+//            public void onReturn() {
+//                if (jCameraLisenter != null) {
+//                    jCameraLisenter.quit();
+//                }
+//            }
+//        });
+        mCaptureLayout.setLeftClickListener(new ClickListener() {
+            @Override
+            public void onClick() {
+                if (leftClickListener != null) {
+                    leftClickListener.onClick();
+                }
+            }
+        });
+        mCaptureLayout.setRightClickListener(new ClickListener() {
+            @Override
+            public void onClick() {
+                if (rightClickListener != null) {
+                    rightClickListener.onClick();
+                }
+            }
+        });
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        float widthSize = mVideoView.getMeasuredWidth();
+        float heightSize = mVideoView.getMeasuredHeight();
+        if (screenProp == 0) {
+            screenProp = heightSize / widthSize;
+        }
+    }
+
+    @Override
+    public void cameraHasOpened() {
+        CameraInterface.getInstance().doStartPreview(mVideoView.getHolder(), screenProp);
+    }
+
+    //生命周期onResume
+    public void onResume() {
+        LogUtil.i("JCameraView onResume");
+        resetState(TYPE_DEFAULT); //重置状态
+        CameraInterface.getInstance().registerSensorManager(mContext);
+        CameraInterface.getInstance().setSwitchView(mSwitchCamera, mFlashLamp);
+        machine.start(mVideoView.getHolder(), screenProp);
+    }
+
+    //生命周期onPause
+    public void onPause() {
+        LogUtil.i("JCameraView onPause");
+        stopVideo();
+        resetState(TYPE_PICTURE);
+        CameraInterface.getInstance().isPreview(false);
+        CameraInterface.getInstance().unregisterSensorManager(mContext);
+    }
+
+    //SurfaceView生命周期
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        LogUtil.i("JCameraView SurfaceCreated");
+        new Thread() {
+            @Override
+            public void run() {
+                CameraInterface.getInstance().doOpenCamera(JCameraView.this);
+            }
+        }.start();
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        LogUtil.i("JCameraView SurfaceDestroyed");
+        CameraInterface.getInstance().doDestroyCamera();
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                if (event.getPointerCount() == 1) {
+                    //显示对焦指示器
+                    setFocusViewWidthAnimation(event.getX(), event.getY());
+                }
+                if (event.getPointerCount() == 2) {
+                    Log.i("CJT", "ACTION_DOWN = " + 2);
+                }
+                break;
+            case MotionEvent.ACTION_MOVE:
+                if (event.getPointerCount() == 1) {
+                    firstTouch = true;
+                }
+                if (event.getPointerCount() == 2) {
+                    //第一个点
+                    float point_1_X = event.getX(0);
+                    float point_1_Y = event.getY(0);
+                    //第二个点
+                    float point_2_X = event.getX(1);
+                    float point_2_Y = event.getY(1);
+
+                    float result = (float) Math.sqrt(Math.pow(point_1_X - point_2_X, 2) + Math.pow(point_1_Y -
+                            point_2_Y, 2));
+
+                    if (firstTouch) {
+                        firstTouchLength = result;
+                        firstTouch = false;
+                    }
+                    if ((int) (result - firstTouchLength) / zoomGradient != 0) {
+                        firstTouch = true;
+                        machine.zoom(result - firstTouchLength, CameraInterface.TYPE_CAPTURE);
+                    }
+//                    Log.i("CJT", "result = " + (result - firstTouchLength));
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                firstTouch = true;
+                break;
+        }
+        return true;
+    }
+
+    //对焦框指示器动画
+    private void setFocusViewWidthAnimation(float x, float y) {
+        machine.foucs(x, y, new CameraInterface.FocusCallback() {
+            @Override
+            public void focusSuccess() {
+                mFoucsView.setVisibility(INVISIBLE);
+            }
+        });
+    }
+
+    private void updateVideoViewSize(float videoWidth, float videoHeight) {
+        if (videoWidth > videoHeight) {
+            LayoutParams videoViewParam;
+            int height = (int) ((videoHeight / videoWidth) * getWidth());
+            videoViewParam = new LayoutParams(LayoutParams.MATCH_PARENT, height);
+            videoViewParam.gravity = Gravity.CENTER;
+            mVideoView.setLayoutParams(videoViewParam);
+        }
+    }
+
+    /**************************************************
+     * 对外提供的API                     *
+     **************************************************/
+
+    public void setSaveVideoPath(String path) {
+        CameraInterface.getInstance().setSaveVideoPath(path);
+    }
+
+
+    public void setJCameraLisenter(JCameraListener jCameraLisenter) {
+        this.jCameraLisenter = jCameraLisenter;
+    }
+
+
+    private ErrorListener errorLisenter;
+
+    //启动Camera错误回调
+    public void setErrorLisenter(ErrorListener errorLisenter) {
+        this.errorLisenter = errorLisenter;
+        CameraInterface.getInstance().setErrorLinsenter(errorLisenter);
+    }
+
+    //设置CaptureButton功能(拍照和录像)
+    public void setFeatures(int state) {
+        this.mCaptureLayout.setButtonFeatures(state);
+    }
+
+    //设置录制质量
+    public void setMediaQuality(int quality) {
+        CameraInterface.getInstance().setMediaQuality(quality);
+    }
+
+    @Override
+    public void resetState(int type) {
+        switch (type) {
+            case TYPE_VIDEO:
+                stopVideo();    //停止播放
+                //初始化VideoView
+                FileUtil.deleteFile(videoUrl);
+                mVideoView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+                machine.start(mVideoView.getHolder(), screenProp);
+                break;
+            case TYPE_PICTURE:
+                mPhoto.setVisibility(INVISIBLE);
+                break;
+            case TYPE_SHORT:
+                break;
+            case TYPE_DEFAULT:
+                mVideoView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+                break;
+        }
+        mSwitchCamera.setVisibility(VISIBLE);
+        mFlashLamp.setVisibility(VISIBLE);
+        mCaptureLayout.resetCaptureLayout();
+    }
+
+    @Override
+    public void confirmState(int type) {
+        switch (type) {
+            case TYPE_VIDEO:
+                stopVideo();    //停止播放
+                mVideoView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+                machine.start(mVideoView.getHolder(), screenProp);
+                if (jCameraLisenter != null) {
+                    jCameraLisenter.recordSuccess(videoUrl, firstFrame);
+                }
+                break;
+            case TYPE_PICTURE:
+                mPhoto.setVisibility(INVISIBLE);
+                if (jCameraLisenter != null) {
+                    jCameraLisenter.captureSuccess(captureBitmap);
+                }
+                break;
+            case TYPE_SHORT:
+                break;
+            case TYPE_DEFAULT:
+                break;
+        }
+        mCaptureLayout.resetCaptureLayout();
+    }
+
+    @Override
+    public void showPicture(Bitmap bitmap, boolean isVertical) {
+        if (isVertical) {
+            mPhoto.setScaleType(ImageView.ScaleType.FIT_XY);
+        } else {
+            mPhoto.setScaleType(ImageView.ScaleType.FIT_CENTER);
+        }
+        captureBitmap = bitmap;
+        mPhoto.setImageBitmap(bitmap);
+        mPhoto.setVisibility(VISIBLE);
+        mCaptureLayout.startAlphaAnimation();
+        mCaptureLayout.startTypeBtnAnimator();
+    }
+
+    @Override
+    public void playVideo(Bitmap firstFrame, final String url) {
+        videoUrl = url;
+        JCameraView.this.firstFrame = firstFrame;
+        new Thread(new Runnable() {
+            @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+            @Override
+            public void run() {
+                try {
+                    if (mMediaPlayer == null) {
+                        mMediaPlayer = new MediaPlayer();
+                    } else {
+                        mMediaPlayer.reset();
+                    }
+                    mMediaPlayer.setDataSource(url);
+                    mMediaPlayer.setSurface(mVideoView.getHolder().getSurface());
+                    mMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT);
+                    mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+                    mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer
+                            .OnVideoSizeChangedListener() {
+                        @Override
+                        public void
+                        onVideoSizeChanged(MediaPlayer mp, int width, int height) {
+                            updateVideoViewSize(mMediaPlayer.getVideoWidth(), mMediaPlayer
+                                    .getVideoHeight());
+                        }
+                    });
+                    mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+                        @Override
+                        public void onPrepared(MediaPlayer mp) {
+                            mMediaPlayer.start();
+                        }
+                    });
+                    mMediaPlayer.setLooping(true);
+                    mMediaPlayer.prepare();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }).start();
+    }
+
+    @Override
+    public void stopVideo() {
+        if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
+            mMediaPlayer.stop();
+            mMediaPlayer.release();
+            mMediaPlayer = null;
+        }
+    }
+
+    @Override
+    public void setTip(String tip) {
+        mCaptureLayout.setTip(tip);
+    }
+
+    @Override
+    public void startPreviewCallback() {
+        LogUtil.i("startPreviewCallback");
+        handlerFoucs(mFoucsView.getWidth() / 2, mFoucsView.getHeight() / 2);
+    }
+
+    @Override
+    public boolean handlerFoucs(float x, float y) {
+        if (y > mCaptureLayout.getTop()) {
+            return false;
+        }
+        mFoucsView.setVisibility(VISIBLE);
+        if (x < mFoucsView.getWidth() / 2) {
+            x = mFoucsView.getWidth() / 2;
+        }
+        if (x > layout_width - mFoucsView.getWidth() / 2) {
+            x = layout_width - mFoucsView.getWidth() / 2;
+        }
+        if (y < mFoucsView.getWidth() / 2) {
+            y = mFoucsView.getWidth() / 2;
+        }
+        if (y > mCaptureLayout.getTop() - mFoucsView.getWidth() / 2) {
+            y = mCaptureLayout.getTop() - mFoucsView.getWidth() / 2;
+        }
+        mFoucsView.setX(x - mFoucsView.getWidth() / 2);
+        mFoucsView.setY(y - mFoucsView.getHeight() / 2);
+        ObjectAnimator scaleX = ObjectAnimator.ofFloat(mFoucsView, "scaleX", 1, 0.6f);
+        ObjectAnimator scaleY = ObjectAnimator.ofFloat(mFoucsView, "scaleY", 1, 0.6f);
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(mFoucsView, "alpha", 1f, 0.4f, 1f, 0.4f, 1f, 0.4f, 1f);
+        AnimatorSet animSet = new AnimatorSet();
+        animSet.play(scaleX).with(scaleY).before(alpha);
+        animSet.setDuration(400);
+        animSet.start();
+        return true;
+    }
+
+    public void setLeftClickListener(ClickListener clickListener) {
+        this.leftClickListener = clickListener;
+    }
+
+    public void setRightClickListener(ClickListener clickListener) {
+        this.rightClickListener = clickListener;
+    }
+
+    private void setFlashRes() {
+        switch (type_flash) {
+            case TYPE_FLASH_AUTO:
+                mFlashLamp.setImageResource(R.drawable.ic_flash_auto);
+                machine.flash(Camera.Parameters.FLASH_MODE_AUTO);
+                break;
+            case TYPE_FLASH_ON:
+                mFlashLamp.setImageResource(R.drawable.ic_flash_on);
+                machine.flash(Camera.Parameters.FLASH_MODE_ON);
+                break;
+            case TYPE_FLASH_OFF:
+                mFlashLamp.setImageResource(R.drawable.ic_flash_off);
+                machine.flash(Camera.Parameters.FLASH_MODE_OFF);
+                break;
+        }
+    }
+}

+ 63 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/ReturnButton.java

@@ -0,0 +1,63 @@
+package com.cjt2325.cameralibrary;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.view.View;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐 445263848@qq.com
+ * 版    本:1.0.4
+ * 创建日期:2017/4/26
+ * 描    述:向下箭头的退出按钮
+ * =====================================
+ */
+public class ReturnButton extends View {
+
+    private int size;
+
+    private int center_X;
+    private int center_Y;
+    private float strokeWidth;
+
+    private Paint paint;
+    Path path;
+
+    public ReturnButton(Context context, int size) {
+        this(context);
+        this.size = size;
+        center_X = size / 2;
+        center_Y = size / 2;
+
+        strokeWidth = size / 15f;
+
+        paint = new Paint();
+        paint.setAntiAlias(true);
+        paint.setColor(Color.WHITE);
+        paint.setStyle(Paint.Style.STROKE);
+        paint.setStrokeWidth(strokeWidth);
+
+        path = new Path();
+    }
+
+    public ReturnButton(Context context) {
+        super(context);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        setMeasuredDimension(size, size / 2);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        path.moveTo(strokeWidth, strokeWidth/2);
+        path.lineTo(center_X, center_Y - strokeWidth/2);
+        path.lineTo(size - strokeWidth, strokeWidth/2);
+        canvas.drawPath(path, paint);
+    }
+}

+ 109 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/TypeButton.java

@@ -0,0 +1,109 @@
+package com.cjt2325.cameralibrary;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.view.View;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐 445263848@qq.com
+ * 版    本:1.0.4
+ * 创建日期:2017/4/26
+ * 描    述:拍照或录制完成后弹出的确认和返回按钮
+ * =====================================
+ */
+public class TypeButton extends View{
+    public static final int TYPE_CANCEL = 0x001;
+    public static final int TYPE_CONFIRM = 0x002;
+    private int button_type;
+    private int button_size;
+
+    private float center_X;
+    private float center_Y;
+    private float button_radius;
+
+    private Paint mPaint;
+    private Path path;
+    private float strokeWidth;
+
+    private float index;
+    private RectF rectF;
+
+    public TypeButton(Context context) {
+        super(context);
+    }
+
+    public TypeButton(Context context, int type, int size) {
+        super(context);
+        this.button_type = type;
+        button_size = size;
+        button_radius = size / 2.0f;
+        center_X = size / 2.0f;
+        center_Y = size / 2.0f;
+
+        mPaint = new Paint();
+        path = new Path();
+        strokeWidth = size / 50f;
+        index = button_size / 12f;
+        rectF = new RectF(center_X, center_Y - index, center_X + index * 2, center_Y + index);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(button_size, button_size);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        //如果类型为取消,则绘制内部为返回箭头
+        if (button_type == TYPE_CANCEL) {
+            mPaint.setAntiAlias(true);
+            mPaint.setColor(0xEEDCDCDC);
+            mPaint.setStyle(Paint.Style.FILL);
+            canvas.drawCircle(center_X, center_Y, button_radius, mPaint);
+
+            mPaint.setColor(Color.BLACK);
+            mPaint.setStyle(Paint.Style.STROKE);
+            mPaint.setStrokeWidth(strokeWidth);
+
+            path.moveTo(center_X - index / 7, center_Y + index);
+            path.lineTo(center_X + index, center_Y + index);
+
+            path.arcTo(rectF, 90, -180);
+            path.lineTo(center_X - index, center_Y - index);
+            canvas.drawPath(path, mPaint);
+            mPaint.setStyle(Paint.Style.FILL);
+            path.reset();
+            path.moveTo(center_X - index, (float) (center_Y - index * 1.5));
+            path.lineTo(center_X - index, (float) (center_Y - index / 2.3));
+            path.lineTo((float) (center_X - index * 1.6), center_Y - index);
+            path.close();
+            canvas.drawPath(path, mPaint);
+
+        }
+        //如果类型为确认,则绘制绿色勾
+        if (button_type == TYPE_CONFIRM) {
+            mPaint.setAntiAlias(true);
+            mPaint.setColor(0xFFFFFFFF);
+            mPaint.setStyle(Paint.Style.FILL);
+            canvas.drawCircle(center_X, center_Y, button_radius, mPaint);
+            mPaint.setAntiAlias(true);
+            mPaint.setStyle(Paint.Style.STROKE);
+            mPaint.setColor(0xFF00CC00);
+            mPaint.setStrokeWidth(strokeWidth);
+
+            path.moveTo(center_X - button_size / 6f, center_Y);
+            path.lineTo(center_X - button_size / 21.2f, center_Y + button_size / 7.7f);
+            path.lineTo(center_X + button_size / 4.0f, center_Y - button_size / 8.5f);
+            path.lineTo(center_X - button_size / 21.2f, center_Y + button_size / 9.4f);
+            path.close();
+            canvas.drawPath(path, mPaint);
+        }
+    }
+}

+ 20 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/listener/CaptureListener.java

@@ -0,0 +1,20 @@
+package com.cjt2325.cameralibrary.listener;
+
+/**
+ * create by CJT2325
+ * 445263848@qq.com.
+ */
+
+public interface CaptureListener {
+    void takePictures();
+
+    void recordShort(long time);
+
+    void recordStart();
+
+    void recordEnd(long time);
+
+    void recordZoom(float zoom);
+
+    void recordError();
+}

+ 13 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/listener/ClickListener.java

@@ -0,0 +1,13 @@
+package com.cjt2325.cameralibrary.listener;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.9
+ * 创建日期:2017/10/7
+ * 描    述:
+ * =====================================
+ */
+public interface ClickListener {
+    void onClick();
+}

+ 14 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/listener/ErrorListener.java

@@ -0,0 +1,14 @@
+package com.cjt2325.cameralibrary.listener;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/6/5
+ * 描    述:
+ * =====================================
+ */
+public interface ErrorListener {
+    void onError();
+    void AudioPermissionError();
+}

+ 19 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/listener/JCameraListener.java

@@ -0,0 +1,19 @@
+package com.cjt2325.cameralibrary.listener;
+
+import android.graphics.Bitmap;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/4/26
+ * 描    述:
+ * =====================================
+ */
+public interface JCameraListener {
+
+    void captureSuccess(Bitmap bitmap);
+
+    void recordSuccess(String url, Bitmap firstFrame);
+
+}

+ 13 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/listener/ResultListener.java

@@ -0,0 +1,13 @@
+package com.cjt2325.cameralibrary.listener;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/9/8
+ * 描    述:
+ * =====================================
+ */
+public interface ResultListener {
+    void callback();
+}

+ 13 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/listener/ReturnListener.java

@@ -0,0 +1,13 @@
+package com.cjt2325.cameralibrary.listener;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/4/26
+ * 描    述:
+ * =====================================
+ */
+public interface ReturnListener {
+    void onReturn();
+}

+ 15 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/listener/TypeListener.java

@@ -0,0 +1,15 @@
+package com.cjt2325.cameralibrary.listener;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/4/25
+ * 描    述:
+ * =====================================
+ */
+public interface TypeListener {
+    void cancel();
+
+    void confirm();
+}

+ 89 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/state/BorrowPictureState.java

@@ -0,0 +1,89 @@
+package com.cjt2325.cameralibrary.state;
+
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import com.cjt2325.cameralibrary.CameraInterface;
+import com.cjt2325.cameralibrary.JCameraView;
+import com.cjt2325.cameralibrary.util.LogUtil;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/9/8
+ * 描    述:
+ * =====================================
+ */
+public class BorrowPictureState implements State {
+    private final String TAG = "BorrowPictureState";
+    private CameraMachine machine;
+
+    public BorrowPictureState(CameraMachine machine) {
+        this.machine = machine;
+    }
+
+    @Override
+    public void start(SurfaceHolder holder, float screenProp) {
+        CameraInterface.getInstance().doStartPreview(holder, screenProp);
+        machine.setState(machine.getPreviewState());
+    }
+
+    @Override
+    public void stop() {
+
+    }
+
+
+    @Override
+    public void foucs(float x, float y, CameraInterface.FocusCallback callback) {
+    }
+
+    @Override
+    public void swtich(SurfaceHolder holder, float screenProp) {
+
+    }
+
+    @Override
+    public void restart() {
+
+    }
+
+    @Override
+    public void capture() {
+
+    }
+
+    @Override
+    public void record(Surface surface,float screenProp) {
+
+    }
+
+    @Override
+    public void stopRecord(boolean isShort, long time) {
+    }
+
+    @Override
+    public void cancle(SurfaceHolder holder, float screenProp) {
+        CameraInterface.getInstance().doStartPreview(holder, screenProp);
+        machine.getView().resetState(JCameraView.TYPE_PICTURE);
+        machine.setState(machine.getPreviewState());
+    }
+
+    @Override
+    public void confirm() {
+        machine.getView().confirmState(JCameraView.TYPE_PICTURE);
+        machine.setState(machine.getPreviewState());
+    }
+
+    @Override
+    public void zoom(float zoom, int type) {
+        LogUtil.i(TAG, "zoom");
+    }
+
+    @Override
+    public void flash(String mode) {
+
+    }
+
+}

+ 89 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/state/BorrowVideoState.java

@@ -0,0 +1,89 @@
+package com.cjt2325.cameralibrary.state;
+
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import com.cjt2325.cameralibrary.CameraInterface;
+import com.cjt2325.cameralibrary.JCameraView;
+import com.cjt2325.cameralibrary.util.LogUtil;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/9/8
+ * 描    述:
+ * =====================================
+ */
+public class BorrowVideoState implements State {
+    private final String TAG = "BorrowVideoState";
+    private CameraMachine machine;
+
+    public BorrowVideoState(CameraMachine machine) {
+        this.machine = machine;
+    }
+
+    @Override
+    public void start(SurfaceHolder holder, float screenProp) {
+        CameraInterface.getInstance().doStartPreview(holder, screenProp);
+        machine.setState(machine.getPreviewState());
+    }
+
+    @Override
+    public void stop() {
+
+    }
+
+    @Override
+    public void foucs(float x, float y, CameraInterface.FocusCallback callback) {
+
+    }
+
+
+    @Override
+    public void swtich(SurfaceHolder holder, float screenProp) {
+
+    }
+
+    @Override
+    public void restart() {
+
+    }
+
+    @Override
+    public void capture() {
+
+    }
+
+    @Override
+    public void record(Surface surface, float screenProp) {
+
+    }
+
+    @Override
+    public void stopRecord(boolean isShort, long time) {
+
+    }
+
+    @Override
+    public void cancle(SurfaceHolder holder, float screenProp) {
+        machine.getView().resetState(JCameraView.TYPE_VIDEO);
+        machine.setState(machine.getPreviewState());
+    }
+
+    @Override
+    public void confirm() {
+        machine.getView().confirmState(JCameraView.TYPE_VIDEO);
+        machine.setState(machine.getPreviewState());
+    }
+
+    @Override
+    public void zoom(float zoom, int type) {
+        LogUtil.i(TAG, "zoom");
+    }
+
+    @Override
+    public void flash(String mode) {
+
+    }
+}

+ 133 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/state/CameraMachine.java

@@ -0,0 +1,133 @@
+package com.cjt2325.cameralibrary.state;
+
+import android.content.Context;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import com.cjt2325.cameralibrary.CameraInterface;
+import com.cjt2325.cameralibrary.view.CameraView;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/9/8
+ * 描    述:
+ * =====================================
+ */
+public class CameraMachine implements State {
+
+
+    private Context context;
+    private State state;
+    private CameraView view;
+//    private CameraInterface.CameraOpenOverCallback cameraOpenOverCallback;
+
+    private State previewState;       //浏览状态(空闲)
+    private State borrowPictureState; //浏览图片
+    private State borrowVideoState;   //浏览视频
+
+    public CameraMachine(Context context, CameraView view, CameraInterface.CameraOpenOverCallback
+            cameraOpenOverCallback) {
+        this.context = context;
+        previewState = new PreviewState(this);
+        borrowPictureState = new BorrowPictureState(this);
+        borrowVideoState = new BorrowVideoState(this);
+        //默认设置为空闲状态
+        this.state = previewState;
+//        this.cameraOpenOverCallback = cameraOpenOverCallback;
+        this.view = view;
+    }
+
+    public CameraView getView() {
+        return view;
+    }
+
+    public Context getContext() {
+        return context;
+    }
+
+    public void setState(State state) {
+        this.state = state;
+    }
+
+    //获取浏览图片状态
+    State getBorrowPictureState() {
+        return borrowPictureState;
+    }
+
+    //获取浏览视频状态
+    State getBorrowVideoState() {
+        return borrowVideoState;
+    }
+
+    //获取空闲状态
+    State getPreviewState() {
+        return previewState;
+    }
+
+    @Override
+    public void start(SurfaceHolder holder, float screenProp) {
+        state.start(holder, screenProp);
+    }
+
+    @Override
+    public void stop() {
+        state.stop();
+    }
+
+    @Override
+    public void foucs(float x, float y, CameraInterface.FocusCallback callback) {
+        state.foucs(x, y, callback);
+    }
+
+    @Override
+    public void swtich(SurfaceHolder holder, float screenProp) {
+        state.swtich(holder, screenProp);
+    }
+
+    @Override
+    public void restart() {
+        state.restart();
+    }
+
+    @Override
+    public void capture() {
+        state.capture();
+    }
+
+    @Override
+    public void record(Surface surface, float screenProp) {
+        state.record(surface, screenProp);
+    }
+
+    @Override
+    public void stopRecord(boolean isShort, long time) {
+        state.stopRecord(isShort, time);
+    }
+
+    @Override
+    public void cancle(SurfaceHolder holder, float screenProp) {
+        state.cancle(holder, screenProp);
+    }
+
+    @Override
+    public void confirm() {
+        state.confirm();
+    }
+
+
+    @Override
+    public void zoom(float zoom, int type) {
+        state.zoom(zoom, type);
+    }
+
+    @Override
+    public void flash(String mode) {
+        state.flash(mode);
+    }
+
+    public State getState() {
+        return this.state;
+    }
+}

+ 109 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/state/PreviewState.java

@@ -0,0 +1,109 @@
+package com.cjt2325.cameralibrary.state;
+
+import android.graphics.Bitmap;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import com.cjt2325.cameralibrary.CameraInterface;
+import com.cjt2325.cameralibrary.JCameraView;
+import com.cjt2325.cameralibrary.util.LogUtil;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/9/8
+ * 描    述:空闲状态
+ * =====================================
+ */
+class PreviewState implements State {
+    public static final String TAG = "PreviewState";
+
+    private CameraMachine machine;
+
+    PreviewState(CameraMachine machine) {
+        this.machine = machine;
+    }
+
+    @Override
+    public void start(SurfaceHolder holder, float screenProp) {
+        CameraInterface.getInstance().doStartPreview(holder, screenProp);
+    }
+
+    @Override
+    public void stop() {
+        CameraInterface.getInstance().doStopPreview();
+    }
+
+
+    @Override
+    public void foucs(float x, float y, CameraInterface.FocusCallback callback) {
+        LogUtil.i("preview state foucs");
+        if (machine.getView().handlerFoucs(x, y)) {
+            CameraInterface.getInstance().handleFocus(machine.getContext(), x, y, callback);
+        }
+    }
+
+    @Override
+    public void swtich(SurfaceHolder holder, float screenProp) {
+        CameraInterface.getInstance().switchCamera(holder, screenProp);
+    }
+
+    @Override
+    public void restart() {
+
+    }
+
+    @Override
+    public void capture() {
+        CameraInterface.getInstance().takePicture(new CameraInterface.TakePictureCallback() {
+            @Override
+            public void captureResult(Bitmap bitmap, boolean isVertical) {
+                machine.getView().showPicture(bitmap, isVertical);
+                machine.setState(machine.getBorrowPictureState());
+                LogUtil.i("capture");
+            }
+        });
+    }
+
+    @Override
+    public void record(Surface surface, float screenProp) {
+        CameraInterface.getInstance().startRecord(surface, screenProp, null);
+    }
+
+    @Override
+    public void stopRecord(final boolean isShort, long time) {
+        CameraInterface.getInstance().stopRecord(isShort, new CameraInterface.StopRecordCallback() {
+            @Override
+            public void recordResult(String url, Bitmap firstFrame) {
+                if (isShort) {
+                    machine.getView().resetState(JCameraView.TYPE_SHORT);
+                } else {
+                    machine.getView().playVideo(firstFrame, url);
+                    machine.setState(machine.getBorrowVideoState());
+                }
+            }
+        });
+    }
+
+    @Override
+    public void cancle(SurfaceHolder holder, float screenProp) {
+        LogUtil.i("浏览状态下,没有 cancle 事件");
+    }
+
+    @Override
+    public void confirm() {
+        LogUtil.i("浏览状态下,没有 confirm 事件");
+    }
+
+    @Override
+    public void zoom(float zoom, int type) {
+        LogUtil.i(TAG, "zoom");
+        CameraInterface.getInstance().setZoom(zoom, type);
+    }
+
+    @Override
+    public void flash(String mode) {
+        CameraInterface.getInstance().setFlashMode(mode);
+    }
+}

+ 41 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/state/State.java

@@ -0,0 +1,41 @@
+package com.cjt2325.cameralibrary.state;
+
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import com.cjt2325.cameralibrary.CameraInterface;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/9/8
+ * 描    述:
+ * =====================================
+ */
+public interface State {
+
+    void start(SurfaceHolder holder, float screenProp);
+
+    void stop();
+
+    void foucs(float x, float y, CameraInterface.FocusCallback callback);
+
+    void swtich(SurfaceHolder holder, float screenProp);
+
+    void restart();
+
+    void capture();
+
+    void record(Surface surface, float screenProp);
+
+    void stopRecord(boolean isShort, long time);
+
+    void cancle(SurfaceHolder holder, float screenProp);
+
+    void confirm();
+
+    void zoom(float zoom, int type);
+
+    void flash(String mode);
+}

+ 52 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/util/AngleUtil.java

@@ -0,0 +1,52 @@
+package com.cjt2325.cameralibrary.util;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/5/2
+ * 描    述:
+ * =====================================
+ */
+public class AngleUtil {
+    public static int getSensorAngle(float x, float y) {
+        if (Math.abs(x) > Math.abs(y)) {
+            /**
+             * 横屏倾斜角度比较大
+             */
+            if (x > 4) {
+                /**
+                 * 左边倾斜
+                 */
+                return 270;
+            } else if (x < -4) {
+                /**
+                 * 右边倾斜
+                 */
+                return 90;
+            } else {
+                /**
+                 * 倾斜角度不够大
+                 */
+                return 0;
+            }
+        } else {
+            if (y > 7) {
+                /**
+                 * 左边倾斜
+                 */
+                return 0;
+            } else if (y < -7) {
+                /**
+                 * 右边倾斜
+                 */
+                return 180;
+            } else {
+                /**
+                 * 倾斜角度不够大
+                 */
+                return 0;
+            }
+        }
+    }
+}

+ 24 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/util/AudioUtil.java

@@ -0,0 +1,24 @@
+package com.cjt2325.cameralibrary.util;
+
+import android.content.Context;
+import android.media.AudioManager;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/4/26
+ * 描    述:
+ * =====================================
+ */
+public class AudioUtil {
+    public static void setAudioManage(Context context) {
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setStreamMute(AudioManager.STREAM_SYSTEM, true);
+        audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
+        audioManager.setStreamVolume(AudioManager.STREAM_ALARM, 0, 0);
+        audioManager.setStreamVolume(AudioManager.STREAM_DTMF, 0, 0);
+        audioManager.setStreamVolume(AudioManager.STREAM_NOTIFICATION, 0, 0);
+        audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
+    }
+}

+ 158 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/util/CameraParamUtil.java

@@ -0,0 +1,158 @@
+package com.cjt2325.cameralibrary.util;
+
+import android.content.Context;
+import android.hardware.Camera;
+import android.util.Log;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/4/25
+ * 描    述:
+ * =====================================
+ */
+public class CameraParamUtil {
+    private static final String TAG = "JCameraView";
+    private CameraSizeComparator sizeComparator = new CameraSizeComparator();
+    private static CameraParamUtil cameraParamUtil = null;
+
+    private CameraParamUtil() {
+
+    }
+
+    public static CameraParamUtil getInstance() {
+        if (cameraParamUtil == null) {
+            cameraParamUtil = new CameraParamUtil();
+            return cameraParamUtil;
+        } else {
+            return cameraParamUtil;
+        }
+    }
+
+    public Camera.Size getPreviewSize(List<Camera.Size> list, int th, float rate) {
+        Collections.sort(list, sizeComparator);
+        int i = 0;
+        for (Camera.Size s : list) {
+            if ((s.width > th) && equalRate(s, rate)) {
+                Log.i(TAG, "MakeSure Preview :w = " + s.width + " h = " + s.height);
+                break;
+            }
+            i++;
+        }
+        if (i == list.size()) {
+            return getBestSize(list, rate);
+        } else {
+            return list.get(i);
+        }
+    }
+
+    public Camera.Size getPictureSize(List<Camera.Size> list, int th, float rate) {
+        Collections.sort(list, sizeComparator);
+        int i = 0;
+        for (Camera.Size s : list) {
+            if ((s.width > th) && equalRate(s, rate)) {
+                Log.i(TAG, "MakeSure Picture :w = " + s.width + " h = " + s.height);
+                break;
+            }
+            i++;
+        }
+        if (i == list.size()) {
+            return getBestSize(list, rate);
+        } else {
+            return list.get(i);
+        }
+    }
+
+    private Camera.Size getBestSize(List<Camera.Size> list, float rate) {
+        float previewDisparity = 100;
+        int index = 0;
+        for (int i = 0; i < list.size(); i++) {
+            Camera.Size cur = list.get(i);
+            float prop = (float) cur.width / (float) cur.height;
+            if (Math.abs(rate - prop) < previewDisparity) {
+                previewDisparity = Math.abs(rate - prop);
+                index = i;
+            }
+        }
+        return list.get(index);
+    }
+
+
+    private boolean equalRate(Camera.Size s, float rate) {
+        float r = (float) (s.width) / (float) (s.height);
+        return Math.abs(r - rate) <= 0.2;
+    }
+
+    public boolean isSupportedFocusMode(List<String> focusList, String focusMode) {
+        for (int i = 0; i < focusList.size(); i++) {
+            if (focusMode.equals(focusList.get(i))) {
+                Log.i(TAG, "FocusMode supported " + focusMode);
+                return true;
+            }
+        }
+        Log.i(TAG, "FocusMode not supported " + focusMode);
+        return false;
+    }
+
+    public boolean isSupportedPictureFormats(List<Integer> supportedPictureFormats, int jpeg) {
+        for (int i = 0; i < supportedPictureFormats.size(); i++) {
+            if (jpeg == supportedPictureFormats.get(i)) {
+                Log.i(TAG, "Formats supported " + jpeg);
+                return true;
+            }
+        }
+        Log.i(TAG, "Formats not supported " + jpeg);
+        return false;
+    }
+
+    private class CameraSizeComparator implements Comparator<Camera.Size> {
+        public int compare(Camera.Size lhs, Camera.Size rhs) {
+            if (lhs.width == rhs.width) {
+                return 0;
+            } else if (lhs.width > rhs.width) {
+                return 1;
+            } else {
+                return -1;
+            }
+        }
+
+    }
+
+    public int getCameraDisplayOrientation(Context context, int cameraId) {
+        android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
+        android.hardware.Camera.getCameraInfo(cameraId, info);
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        int rotation = wm.getDefaultDisplay().getRotation();
+        int degrees = 0;
+        switch (rotation) {
+            case Surface.ROTATION_0:
+                degrees = 0;
+                break;
+            case Surface.ROTATION_90:
+                degrees = 90;
+                break;
+            case Surface.ROTATION_180:
+                degrees = 180;
+                break;
+            case Surface.ROTATION_270:
+                degrees = 270;
+                break;
+        }
+        int result;
+        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+            result = (info.orientation + degrees) % 360;
+            result = (360 - result) % 360;   // compensate the mirror
+        } else {
+            // back-facing
+            result = (info.orientation - degrees + 360) % 360;
+        }
+        return result;
+    }
+}

+ 104 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/util/CheckPermission.java

@@ -0,0 +1,104 @@
+package com.cjt2325.cameralibrary.util;
+
+import android.hardware.Camera;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.util.Log;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/6/8
+ * 描    述:
+ * =====================================
+ */
+public class CheckPermission {
+    public static final int STATE_RECORDING = -1;
+    public static final int STATE_NO_PERMISSION = -2;
+    public static final int STATE_SUCCESS = 1;
+
+    /**
+     * 用于检测是否具有录音权限
+     *
+     * @return
+     */
+    public static int getRecordState() {
+        int minBuffer = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat
+                .ENCODING_PCM_16BIT);
+        AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, 44100, AudioFormat
+                .CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, (minBuffer * 100));
+        short[] point = new short[minBuffer];
+        int readSize = 0;
+        try {
+
+            audioRecord.startRecording();//检测是否可以进入初始化状态
+        } catch (Exception e) {
+            if (audioRecord != null) {
+                audioRecord.release();
+                audioRecord = null;
+            }
+            return STATE_NO_PERMISSION;
+        }
+        if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
+            //6.0以下机型都会返回此状态,故使用时需要判断bulid版本
+            //检测是否在录音中
+            if (audioRecord != null) {
+                audioRecord.stop();
+                audioRecord.release();
+                audioRecord = null;
+                Log.d("CheckAudioPermission", "录音机被占用");
+            }
+            return STATE_RECORDING;
+        } else {
+            //检测是否可以获取录音结果
+
+            readSize = audioRecord.read(point, 0, point.length);
+
+
+            if (readSize <= 0) {
+                if (audioRecord != null) {
+                    audioRecord.stop();
+                    audioRecord.release();
+                    audioRecord = null;
+
+                }
+                Log.d("CheckAudioPermission", "录音的结果为空");
+                return STATE_NO_PERMISSION;
+
+            } else {
+                if (audioRecord != null) {
+                    audioRecord.stop();
+                    audioRecord.release();
+                    audioRecord = null;
+
+                }
+
+                return STATE_SUCCESS;
+            }
+        }
+    }
+
+    public synchronized static boolean isCameraUseable(int cameraID) {
+        boolean canUse = true;
+        Camera mCamera = null;
+        try {
+            mCamera = Camera.open(cameraID);
+            // setParameters 是针对魅族MX5。MX5通过Camera.open()拿到的Camera对象不为null
+            Camera.Parameters mParameters = mCamera.getParameters();
+            mCamera.setParameters(mParameters);
+        } catch (Exception e) {
+            e.printStackTrace();
+            canUse = false;
+        } finally {
+            if (mCamera != null) {
+                mCamera.release();
+            } else {
+                canUse = false;
+            }
+            mCamera = null;
+        }
+        return canUse;
+    }
+}

+ 46 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/util/DeviceUtil.java

@@ -0,0 +1,46 @@
+package com.cjt2325.cameralibrary.util;
+
+import android.os.Build;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/6/9
+ * 描    述:
+ * =====================================
+ */
+public class DeviceUtil {
+
+    private static String[] huaweiRongyao = {
+            "hwH60",    //荣耀6
+            "hwPE",     //荣耀6 plus
+            "hwH30",    //3c
+            "hwHol",    //3c畅玩版
+            "hwG750",   //3x
+            "hw7D",      //x1
+            "hwChe2",      //x1
+    };
+
+    public static String getDeviceInfo() {
+        String handSetInfo =
+                "手机型号:" + Build.DEVICE +
+                        "\n系统版本:" + Build.VERSION.RELEASE +
+                        "\nSDK版本:" + Build.VERSION.SDK_INT;
+        return handSetInfo;
+    }
+
+    public static String getDeviceModel() {
+        return Build.DEVICE;
+    }
+
+    public static boolean isHuaWeiRongyao() {
+        int length = huaweiRongyao.length;
+        for (int i = 0; i < length; i++) {
+            if (huaweiRongyao[i].equals(getDeviceModel())) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 70 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/util/FileUtil.java

@@ -0,0 +1,70 @@
+package com.cjt2325.cameralibrary.util;
+
+import android.graphics.Bitmap;
+import android.os.Environment;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/4/25
+ * 描    述:
+ * =====================================
+ */
+public class FileUtil {
+    private static final String TAG = "CJT";
+    private static final File parentPath = Environment.getExternalStorageDirectory();
+    private static String storagePath = "";
+    private static String DST_FOLDER_NAME = "JCamera";
+
+    private static String initPath() {
+        if (storagePath.equals("")) {
+            storagePath = parentPath.getAbsolutePath() + File.separator + DST_FOLDER_NAME;
+            File f = new File(storagePath);
+            if (!f.exists()) {
+                f.mkdir();
+            }
+        }
+        return storagePath;
+    }
+
+    public static String saveBitmap(String dir, Bitmap b) {
+        DST_FOLDER_NAME = dir;
+        String path = initPath();
+        long dataTake = System.currentTimeMillis();
+        String jpegName = path + File.separator + "picture_" + dataTake + ".jpg";
+        try {
+            FileOutputStream fout = new FileOutputStream(jpegName);
+            BufferedOutputStream bos = new BufferedOutputStream(fout);
+            b.compress(Bitmap.CompressFormat.JPEG, 100, bos);
+            bos.flush();
+            bos.close();
+            return jpegName;
+        } catch (IOException e) {
+            e.printStackTrace();
+            return "";
+        }
+    }
+
+    public static boolean deleteFile(String url) {
+        boolean result = false;
+        File file = new File(url);
+        if (file.exists()) {
+            result = file.delete();
+        }
+        return result;
+    }
+
+    public static boolean isExternalStorageWritable() {
+        String state = Environment.getExternalStorageState();
+        if (Environment.MEDIA_MOUNTED.equals(state)) {
+            return true;
+        }
+        return false;
+    }
+}

+ 56 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/util/LogUtil.java

@@ -0,0 +1,56 @@
+package com.cjt2325.cameralibrary.util;
+
+import android.util.Log;
+
+//import static com.cjt2325.cameralibrary.BuildConfig.DEBUG;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/9/7
+ * 描    述:
+ * =====================================
+ */
+public class LogUtil {
+
+    private static boolean DEBUG = true;
+
+    private static final String DEFAULT_TAG = "CJT";
+
+    public static void i(String tag, String msg) {
+//        if (DEBUG)
+        Log.i(tag, msg);
+    }
+
+    public static void v(String tag, String msg) {
+        if (DEBUG)
+            Log.v(tag, msg);
+    }
+
+    public static void d(String tag, String msg) {
+        if (DEBUG)
+            Log.d(tag, msg);
+    }
+
+    public static void e(String tag, String msg) {
+        if (DEBUG)
+            Log.e(tag, msg);
+    }
+
+    public static void i(String msg) {
+        i(DEFAULT_TAG, msg);
+    }
+
+    public static void v(String msg) {
+        v(DEFAULT_TAG, msg);
+    }
+
+    public static void d(String msg) {
+        d(DEFAULT_TAG, msg);
+    }
+
+    public static void e(String msg) {
+        e(DEFAULT_TAG, msg);
+    }
+}

+ 29 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/util/ScreenUtils.java

@@ -0,0 +1,29 @@
+package com.cjt2325.cameralibrary.util;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.view.WindowManager;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/5/25
+ * 描    述:
+ * =====================================
+ */
+public class ScreenUtils {
+    public static int getScreenHeight(Context context) {
+        DisplayMetrics metric = new DisplayMetrics();
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        wm.getDefaultDisplay().getMetrics(metric);
+        return metric.heightPixels;
+    }
+
+    public static int getScreenWidth(Context context) {
+        DisplayMetrics metric = new DisplayMetrics();
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        wm.getDefaultDisplay().getMetrics(metric);
+        return metric.widthPixels;
+    }
+}

+ 29 - 0
cameraview/src/main/java/com/cjt2325/cameralibrary/view/CameraView.java

@@ -0,0 +1,29 @@
+package com.cjt2325.cameralibrary.view;
+
+import android.graphics.Bitmap;
+
+/**
+ * =====================================
+ * 作    者: 陈嘉桐
+ * 版    本:1.1.4
+ * 创建日期:2017/9/8
+ * 描    述:
+ * =====================================
+ */
+public interface CameraView {
+    void resetState(int type);
+
+    void confirmState(int type);
+
+    void showPicture(Bitmap bitmap, boolean isVertical);
+
+    void playVideo(Bitmap firstFrame, String url);
+
+    void stopVideo();
+
+    void setTip(String tip);
+
+    void startPreviewCallback();
+
+    boolean handlerFoucs(float x, float y);
+}

+ 9 - 0
cameraview/src/main/res/drawable/ic_camera.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M9,3L7.17,5L4,5c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2h-3.17L15,3L9,3zM12,18c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,17l1.25,-2.75L16,13l-2.75,-1.25L12,9l-1.25,2.75L8,13l2.75,1.25z"/>
+</vector>

+ 9 - 0
cameraview/src/main/res/drawable/ic_flash_auto.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M3,2v12h3v9l7,-12L9,11l4,-9L3,2zM19,2h-2l-3.2,9h1.9l0.7,-2h3.2l0.7,2h1.9L19,2zM16.85,7.65L18,4l1.15,3.65h-2.3z"/>
+</vector>

+ 9 - 0
cameraview/src/main/res/drawable/ic_flash_off.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M3.27,3L2,4.27l5,5V13h3v9l3.58,-6.14L17.73,20 19,18.73 3.27,3zM17,10h-4l4,-8H7v2.18l8.46,8.46L17,10z"/>
+</vector>

+ 9 - 0
cameraview/src/main/res/drawable/ic_flash_on.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M7,2v11h3v9l7,-12h-4l4,-8z"/>
+</vector>

+ 9 - 0
cameraview/src/main/res/drawable/ic_photo.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
+</vector>

+ 61 - 0
cameraview/src/main/res/layout/camera_view.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             android:background="#000000"
+             android:orientation="vertical">
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <VideoView
+            android:id="@+id/video_preview"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+
+        <ImageView
+            android:id="@+id/image_photo"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:background="#000"
+            android:visibility="invisible"/>
+    </FrameLayout>
+
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="right"
+        android:orientation="horizontal">
+
+        <ImageView
+            android:id="@+id/image_flash"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="16dp"
+            android:src="@drawable/ic_flash_on"/>
+
+        <ImageView
+            android:id="@+id/image_switch"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="16dp"
+            android:src="@drawable/ic_camera"/>
+
+    </LinearLayout>
+
+
+    <com.cjt2325.cameralibrary.CaptureLayout
+        android:id="@+id/capture_layout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom"/>
+
+    <com.cjt2325.cameralibrary.FoucsView
+        android:id="@+id/fouce_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:visibility="invisible"/>
+</FrameLayout>

+ 17 - 0
cameraview/src/main/res/values/attrs.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <attr name="iconSize" format="dimension"/>
+    <attr name="iconMargin" format="dimension"/>
+    <attr name="iconSrc" format="reference"/>
+    <attr name="iconLeft" format="reference"/>
+    <attr name="iconRight" format="reference"/>
+    <attr name="duration_max" format="integer"/>
+    <declare-styleable name="JCameraView">
+        <attr name="iconSize"/>
+        <attr name="iconMargin"/>
+        <attr name="iconSrc"/>
+        <attr name="iconLeft"/>
+        <attr name="iconRight"/>
+        <attr name="duration_max"/>
+    </declare-styleable>
+</resources>

+ 3 - 0
cameraview/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">CameraLibrary</string>
+</resources>

+ 17 - 0
cameraview/src/test/java/cn/wildfirechat/cameraview/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package cn.wildfirechat.cameraview;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}

+ 1 - 0
settings.gradle

@@ -8,6 +8,7 @@ include ':client',
     ':badgeview',
     ":menu",
     ":permission",
+    ':cameraview',
     ':uvccamera',
     ':uikit-aar-dep',
     ':emojilibrary',

+ 1 - 1
uikit-aar-dep/build.gradle

@@ -1,5 +1,5 @@
 configurations.maybeCreate("default")
-artifacts.add("default", file('com.cjt2325.cameralibrary-library-1.1.9.aar'))
+//artifacts.add("default", file('com.cjt2325.cameralibrary-library-1.1.9.aar'))
 artifacts.add("default", file('flexbox-2.0.1.aar'))
 artifacts.add("default", file('com.lqr.adapter-library-1.0.2.aar'))
 artifacts.add("default", file('statusbarutil-library-1.3.5.aar'))

BIN
uikit-aar-dep/com.cjt2325.cameralibrary-library-1.1.9.aar


+ 2 - 0
uikit/build.gradle

@@ -67,8 +67,10 @@ dependencies {
     api project(':menu')
     api project(':permission')
 
+
     implementation project(':emojilibrary')
     implementation project(':imagepicker')
+    implementation project(':cameraview')
 
     api 'androidx.appcompat:appcompat:1.7.0'
     api 'com.google.android.material:material:1.12.0'