com.glenn.cresco.photosharingsample.fragments.PhotoCameraFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.glenn.cresco.photosharingsample.fragments.PhotoCameraFragment.java

Source

package com.glenn.cresco.photosharingsample.fragments;

/*
 * Copyright (c) 2014 Rex St. John on behalf of AirPair.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

import android.content.Context;
import android.content.Intent;
import android.hardware.Camera;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;

import com.glenn.cresco.photosharingsample.PhotoSendActivity;
import com.glenn.cresco.photosharingsample.R;
import com.glenn.cresco.photosharingsample.utils.PictureUtils;

import java.util.List;
import java.util.concurrent.Callable;

import bolts.Continuation;
import bolts.Task;
import butterknife.Bind;
import butterknife.ButterKnife;

/**
 * Created by Glenn on 12/01/2016.
 */
public class PhotoCameraFragment extends Fragment {

    public static final String TAG = PhotoCameraFragment.class.getSimpleName();
    public static final String CAMERA_ID_KEY = "camera_id";
    public static final String CAMERA_FLASH_KEY = "flash_mode";
    public static final String IMAGE_INFO = "image_info";

    private static final int PICTURE_SIZE_MAX_WIDTH = 1280;
    private static final int PREVIEW_SIZE_MAX_WIDTH = 640;

    @Bind(R.id.capture_image_button)
    ImageView captureButton;

    @Bind(R.id.flash)
    View flashGroup;

    @Bind(R.id.change_camera)
    ImageView swapCameraBtn;

    // Native camera.
    private Camera mCamera;

    // View to display the camera output.
    private CameraPreview mPreview;

    // Reference to the containing view.
    private View mCameraView;

    /**
     * Default empty constructor.
     */
    public PhotoCameraFragment() {
        super();
    }

    public static PhotoCameraFragment newInstance() {
        return new PhotoCameraFragment();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_photo_camera, container, false);
        ButterKnife.bind(this, view);

        // Create our Preview view and set it as the content of our activity.
        boolean opened = safeCameraOpenInView(view);

        if (!opened) {
            Log.d("CameraGuide", "Error, Camera failed to open");
            return view;
        }

        // Trap the capture button.
        captureButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // get an image from the camera
                mCamera.takePicture(null, null, mPicture);
            }
        });

        return view;
    }

    /**
     * Recommended "safe" way to open the camera.
     *
     * @param view
     * @return
     */
    private boolean safeCameraOpenInView(View view) {
        boolean qOpened;
        releaseCameraAndPreview();
        mCamera = getCameraInstance();
        mCameraView = view;
        qOpened = (mCamera != null);

        if (qOpened) {
            mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera);
            FrameLayout preview = (FrameLayout) view.findViewById(R.id.camera_preview);
            preview.addView(mPreview);
            mPreview.startCameraPreview();
        }
        return qOpened;
    }

    /**
     * Safe method for getting a camera instance.
     *
     * @return
     */
    public static Camera getCameraInstance() {
        Camera c = null;
        try {
            c = Camera.open(); // attempt to get a Camera instance
        } catch (Exception e) {
            e.printStackTrace();
        }
        return c; // returns null if camera is unavailable
    }

    @Override
    public void onPause() {
        super.onPause();
        releaseCameraAndPreview();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        releaseCameraAndPreview();
    }

    /**
     * Clear any existing preview / camera.
     */
    private void releaseCameraAndPreview() {

        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
        if (mPreview != null) {
            mPreview.destroyDrawingCache();
            mPreview.mCamera = null;
        }
    }

    /**
     * Picture Callback for handling a picture capture and saving it out to a file.
     */
    private Camera.PictureCallback mPicture = new Camera.PictureCallback() {

        @Override
        public void onPictureTaken(final byte[] data, Camera camera) {

            Log.d("picture taken", "taken");

            Task.callInBackground(new Callable<Void>() {
                @Override
                public Void call() throws Exception {
                    PictureUtils.saveBitmapToCache(data, getActivity());
                    return null;
                }
            }).onSuccess(new Continuation<Void, Void>() {
                @Override
                public Void then(Task<Void> task) throws Exception {
                    Intent intent = new Intent(getActivity(), PhotoSendActivity.class);
                    startActivity(intent);
                    return null;
                }
            });
        }
    };

    public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

        private static final String TAG = "CameraPreview";

        private Context mContext;
        private SurfaceHolder mHolder;
        private Camera mCamera;
        private List<Camera.Size> mSupportedPreviewSizes;
        private Camera.Size mPreviewSize;

        public CameraPreview(Context context, Camera camera) {
            super(context);
            mContext = context;
            mCamera = camera;

            // supported preview sizes
            mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
            for (Camera.Size str : mSupportedPreviewSizes)
                Log.e(TAG, str.width + "/" + str.height);

            // Install a SurfaceHolder.Callback so we get notified when the
            // underlying surface is created and destroyed.
            mHolder = getHolder();
            mHolder.addCallback(this);
            // deprecated setting, but required on Android versions prior to 3.0
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }

        public void surfaceCreated(SurfaceHolder holder) {
            // empty. surfaceChanged will take care of stuff
        }

        public void surfaceDestroyed(SurfaceHolder holder) {
            // empty. Take care of releasing the Camera preview in your activity.
        }

        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
            Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + h);
            // If your preview can change or rotate, take care of those events here.
            // Make sure to stop the preview before resizing or reformatting it.
            if (mHolder.getSurface() == null) {
                // preview surface does not exist
                return;
            }

            // stop preview before making changes
            try {
                mCamera.stopPreview();
            } catch (Exception e) {
                // ignore: tried to stop a non-existent preview
            }

            // set preview size and make any resize, rotate or reformatting changes here
            // start preview with new settings
            try {
                Camera.Parameters parameters = mCamera.getParameters();
                parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
                mCamera.setParameters(parameters);
                mCamera.setDisplayOrientation(90);
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();

            } catch (Exception e) {
                Log.d(TAG, "Error starting camera preview: " + e.getMessage());
            }
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
            final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

            if (mSupportedPreviewSizes != null) {
                mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
            }

            float ratio;
            if (mPreviewSize.height >= mPreviewSize.width)
                ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
            else
                ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;

            float camHeight = (int) (width * ratio);
            float newCamHeight;
            float newHeightRatio;

            if (camHeight < height) {
                newHeightRatio = (float) height / (float) mPreviewSize.height;
                newCamHeight = (newHeightRatio * camHeight);
                Log.e(TAG, camHeight + " " + height + " " + mPreviewSize.height + " " + newHeightRatio + " "
                        + newCamHeight);
                setMeasuredDimension((int) (width * newHeightRatio), (int) newCamHeight);
                Log.e(TAG,
                        mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | H_ratio - "
                                + newHeightRatio + " | A_width - " + (width * newHeightRatio) + " | A_height - "
                                + newCamHeight);
            } else {
                newCamHeight = camHeight;
                setMeasuredDimension(width, (int) newCamHeight);
                Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio
                        + " | A_width - " + (width) + " | A_height - " + newCamHeight);
            }

            // One of these methods should be used, second method squishes preview slightly
            setMeasuredDimension(width, (int) (width * ratio));
            //        setMeasuredDimension((int) (width * ratio), height);
        }

        private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
            final double ASPECT_TOLERANCE = 0.1;
            double targetRatio = (double) h / w;

            if (sizes == null)
                return null;

            Camera.Size optimalSize = null;
            double minDiff = Double.MAX_VALUE;

            int targetHeight = h;

            for (Camera.Size size : sizes) {
                double ratio = (double) size.height / size.width;
                if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                    continue;

                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }

            if (optimalSize == null) {
                minDiff = Double.MAX_VALUE;
                for (Camera.Size size : sizes) {
                    if (Math.abs(size.height - targetHeight) < minDiff) {
                        optimalSize = size;
                        minDiff = Math.abs(size.height - targetHeight);
                    }
                }
            }

            return optimalSize;
        }

        public void startCameraPreview() {
            try {
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Surface on which the camera projects it's capture results. This is derived both from Google's docs and the
     * excellent StackOverflow answer provided below.
     * <p/>
     * Reference / Credit: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails
        
     class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
        
     // SurfaceHolder
     private SurfaceHolder mHolder;
        
     // Our Camera.
     private Camera mCamera;
        
     // Parent Context.
     private Context mContext;
        
     // Camera Sizing (For rotation, orientation changes)
     private Camera.Size mPreviewSize;
        
     // List of supported preview sizes
     private List<Camera.Size> mSupportedPreviewSizes;
        
     // Flash modes supported by this camera
     private List<String> mSupportedFlashModes;
        
     // View holding this camera.
     private View mCameraView;
        
     public CameraPreview(Context context, Camera camera, View cameraView) {
     super(context);
        
     // Capture the context
     mCameraView = cameraView;
     mContext = context;
     setCamera(camera);
        
     // Install a SurfaceHolder.Callback so we get notified when the
     // underlying surface is created and destroyed.
     mHolder = getHolder();
     mHolder.addCallback(this);
     mHolder.setKeepScreenOn(true);
     // deprecated setting, but required on Android versions prior to 3.0
     mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
     }
        
        
     public void startCameraPreview() {
     try {
     mCamera.setPreviewDisplay(mHolder);
     mCamera.startPreview();
     } catch (Exception e) {
     e.printStackTrace();
     }
     }
        
        
     private void setCamera(Camera camera) {
     // Source: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails
     mCamera = camera;
     mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
     mSupportedFlashModes = mCamera.getParameters().getSupportedFlashModes();
        
     // Set the camera to Auto Flash mode.
     if (mSupportedFlashModes != null && mSupportedFlashModes.contains(Camera.Parameters.FLASH_MODE_AUTO)) {
     Camera.Parameters parameters = mCamera.getParameters();
     parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
     mCamera.setParameters(parameters);
     }
        
     requestLayout();
     }
        
        
     public void surfaceCreated(SurfaceHolder holder) {
     try {
     mCamera.setPreviewDisplay(holder);
     mCamera.setDisplayOrientation(90);
     } catch (IOException e) {
     e.printStackTrace();
     }
     }
        
     public void surfaceDestroyed(SurfaceHolder holder) {
     if (mCamera != null) {
     mCamera.stopPreview();
     }
     }
        
        
     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
     // If your preview can change or rotate, take care of those events here.
     // Make sure to stop the preview before resizing or reformatting it.
        
     if (mHolder.getSurface() == null) {
     // preview surface does not exist
     return;
     }
        
     // stop preview before making changes
     try {
     Camera.Parameters parameters = mCamera.getParameters();
        
     // Set the auto-focus mode to "continuous"
     parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        
     // Preview size must exist.
     if (mPreviewSize != null) {
     Camera.Size previewSize = mPreviewSize;
     parameters.setPreviewSize(previewSize.width, previewSize.height);
     }
        
     mCamera.setParameters(parameters);
     mCamera.startPreview();
     } catch (Exception e) {
     e.printStackTrace();
     }
     }
        
     @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
     final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
     setMeasuredDimension(width, height);
        
     if (mSupportedPreviewSizes != null) {
     mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
     }
     }
        
        
     @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
     // Source: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails
     if (changed) {
     final int width = right - left;
     final int height = bottom - top;
        
     int previewWidth = width;
     int previewHeight = height;
        
     if (mPreviewSize != null) {
        
     previewWidth = mPreviewSize.height;
     previewHeight = mPreviewSize.width;
     mCamera.setDisplayOrientation(90);
     }
        
     final int scaledChildHeight = previewHeight * width / previewWidth;
     mCameraView.layout(0, height - scaledChildHeight, width, height);
     }
     }
        
        
     private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
     final double ASPECT_TOLERANCE = 0.1;
     double targetRatio = (double) h / w;
        
     if (sizes == null) return null;
        
     Camera.Size optimalSize = null;
     double minDiff = Double.MAX_VALUE;
        
     int targetHeight = h;
        
     for (Camera.Size size : sizes) {
     double ratio = (double) size.width / size.height;
     if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
     if (Math.abs(size.height - targetHeight) < minDiff) {
     optimalSize = size;
     minDiff = Math.abs(size.height - targetHeight);
     }
     }
        
     if (optimalSize == null) {
     minDiff = Double.MAX_VALUE;
     for (Camera.Size size : sizes) {
     if (Math.abs(size.height - targetHeight) < minDiff) {
     optimalSize = size;
     minDiff = Math.abs(size.height - targetHeight);
     }
     }
     }
     return optimalSize;
     }
        
     }*/
}