/*** Copyright (c) 2013 CommonsWare, LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package com.commonsware.cwac.camera; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.hardware.Camera; import android.media.CamcorderProfile; import android.media.MediaActionSound; import android.media.MediaRecorder; import android.media.MediaScannerConnection; import android.os.Build; import android.os.Environment; import android.util.Log; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class SimpleCameraHost implements CameraHost { private static final String[] SCAN_TYPES= { "image/jpeg" }; private Context ctxt=null; private int cameraId=-1; private DeviceProfile profile=null; private File photoDirectory=null; private File videoDirectory=null; private RecordingHint recordingHint=null; private boolean mirrorFFC=false; private boolean useFrontFacingCamera=false; private boolean scanSavedImage=true; private boolean useFullBleedPreview=true; private boolean useSingleShotMode=false; public SimpleCameraHost(Context _ctxt) { this.ctxt=_ctxt.getApplicationContext(); } @Override public Camera.Parameters adjustPictureParameters(PictureTransaction xact, Camera.Parameters parameters) { return(parameters); } @Override public Camera.Parameters adjustPreviewParameters(Camera.Parameters parameters) { return(parameters); } @Override public void configureRecorderAudio(int cameraId, MediaRecorder recorder) { recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); } @Override public void configureRecorderOutput(int cameraId, MediaRecorder recorder) { recorder.setOutputFile(getVideoPath().getAbsolutePath()); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public void configureRecorderProfile(int cameraId, MediaRecorder recorder) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB || CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_HIGH)) { recorder.setProfile(CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH)); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_LOW)) { recorder.setProfile(CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW)); } else { throw new IllegalStateException( "cannot find valid CamcorderProfile"); } } @Override public int getCameraId() { if (cameraId == -1) { initCameraId(); } return(cameraId); } private void initCameraId() { int count=Camera.getNumberOfCameras(); int result=-1; if (count > 0) { result=0; // if we have a camera, default to this one Camera.CameraInfo info=new Camera.CameraInfo(); for (int i=0; i < count; i++) { Camera.getCameraInfo(i, info); if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK && !useFrontFacingCamera()) { result=i; break; } else if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT && useFrontFacingCamera()) { result=i; break; } } } cameraId=result; } @Override public DeviceProfile getDeviceProfile() { if (profile == null) { initDeviceProfile(ctxt); } return(profile); } private void initDeviceProfile(Context ctxt) { profile=DeviceProfile.getInstance(ctxt); } @Override public Camera.Size getPictureSize(PictureTransaction xact, Camera.Parameters parameters) { return(CameraUtils.getLargestPictureSize(this, parameters)); } @Override public Camera.Size getPreviewSize(int displayOrientation, int width, int height, Camera.Parameters parameters) { return(CameraUtils.getBestAspectPreviewSize(displayOrientation, width, height, parameters)); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public Camera.Size getPreferredPreviewSizeForVideo(int displayOrientation, int width, int height, Camera.Parameters parameters, Camera.Size deviceHint) { if (deviceHint != null) { return(deviceHint); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { return(parameters.getPreferredPreviewSizeForVideo()); } return(null); } @Override public Camera.ShutterCallback getShutterCallback() { return(null); } @Override public void handleException(Exception e) { Log.e(getClass().getSimpleName(), "Exception in setPreviewDisplay()", e); } @Override public boolean mirrorFFC() { return(mirrorFFC); } @Override public void saveImage(PictureTransaction xact, Bitmap bitmap) { // no-op } @Override public void saveImage(PictureTransaction xact, byte[] image) { File photo=getPhotoPath(); if (photo.exists()) { photo.delete(); } try { FileOutputStream fos=new FileOutputStream(photo.getPath()); BufferedOutputStream bos=new BufferedOutputStream(fos); bos.write(image); bos.flush(); fos.getFD().sync(); bos.close(); if (scanSavedImage()) { MediaScannerConnection.scanFile(ctxt, new String[] { photo.getPath() }, SCAN_TYPES, null); } } catch (java.io.IOException e) { handleException(e); } } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override public void onAutoFocus(boolean success, Camera camera) { if (success && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { new MediaActionSound().play(MediaActionSound.FOCUS_COMPLETE); } } @Override public boolean useSingleShotMode() { return(useSingleShotMode); } @Override public void autoFocusAvailable() { // no-op } @Override public void autoFocusUnavailable() { // no-op } @Override public RecordingHint getRecordingHint() { if (recordingHint == null) { initRecordingHint(); } return(recordingHint); } private void initRecordingHint() { recordingHint=profile.getDefaultRecordingHint(); if (recordingHint==RecordingHint.NONE) { recordingHint=RecordingHint.ANY; } } @Override public void onCameraFail(FailureReason reason) { Log.e("CWAC-Camera", String.format("Camera access failed: %d", reason.value)); } @Override public boolean useFullBleedPreview() { return(useFullBleedPreview); } @Override public float maxPictureCleanupHeapUsage() { return(1.0f); } @Override public void onPreviewFrame(byte [] rawData, Camera camera) { } protected File getPhotoPath() { File dir=getPhotoDirectory(); dir.mkdirs(); return(new File(dir, getPhotoFilename())); } protected File getPhotoDirectory() { if (photoDirectory == null) { initPhotoDirectory(); } return(photoDirectory); } private void initPhotoDirectory() { photoDirectory= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM); } protected String getPhotoFilename() { String ts= new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date()); return("Photo_" + ts + ".jpg"); } protected File getVideoPath() { File dir=getVideoDirectory(); dir.mkdirs(); return(new File(dir, getVideoFilename())); } protected File getVideoDirectory() { if (videoDirectory == null) { initVideoDirectory(); } return(videoDirectory); } private void initVideoDirectory() { videoDirectory= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES); } protected String getVideoFilename() { String ts= new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date()); return("Video_" + ts + ".mp4"); } protected boolean useFrontFacingCamera() { return(useFrontFacingCamera); } protected boolean scanSavedImage() { return(scanSavedImage); } public static class Builder { private SimpleCameraHost host=null; public Builder(Context ctxt) { this(new SimpleCameraHost(ctxt)); } public Builder(SimpleCameraHost host) { this.host=host; } public SimpleCameraHost build() { return(host); } public Builder cameraId(int cameraId) { host.cameraId=cameraId; return(this); } public Builder deviceProfile(DeviceProfile profile) { host.profile=profile; return(this); } public Builder mirrorFFC(boolean mirrorFFC) { host.mirrorFFC=mirrorFFC; return(this); } public Builder photoDirectory(File photoDirectory) { host.photoDirectory=photoDirectory; return(this); } public Builder recordingHint(RecordingHint recordingHint) { host.recordingHint=recordingHint; return(this); } public Builder scanSavedImage(boolean scanSavedImage) { host.scanSavedImage=scanSavedImage; return(this); } public Builder useFrontFacingCamera(boolean useFrontFacingCamera) { host.useFrontFacingCamera=useFrontFacingCamera; return(this); } public Builder useFullBleedPreview(boolean useFullBleedPreview) { host.useFullBleedPreview=useFullBleedPreview; return(this); } public Builder useSingleShotMode(boolean useSingleShotMode) { host.useSingleShotMode=useSingleShotMode; return(this); } public Builder videoDirectory(File videoDirectory) { host.videoDirectory=videoDirectory; return(this); } } }