1*90c8c64dSAndroid Build Coastguard Worker /*
2*90c8c64dSAndroid Build Coastguard Worker  * Copyright (C) 2013 The Android Open Source Project
3*90c8c64dSAndroid Build Coastguard Worker  *
4*90c8c64dSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*90c8c64dSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*90c8c64dSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*90c8c64dSAndroid Build Coastguard Worker  *
8*90c8c64dSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*90c8c64dSAndroid Build Coastguard Worker  *
10*90c8c64dSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*90c8c64dSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*90c8c64dSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*90c8c64dSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*90c8c64dSAndroid Build Coastguard Worker  * limitations under the License.
15*90c8c64dSAndroid Build Coastguard Worker  */
16*90c8c64dSAndroid Build Coastguard Worker 
17*90c8c64dSAndroid Build Coastguard Worker package com.example.android.common.media;
18*90c8c64dSAndroid Build Coastguard Worker 
19*90c8c64dSAndroid Build Coastguard Worker import android.annotation.TargetApi;
20*90c8c64dSAndroid Build Coastguard Worker import android.hardware.Camera;
21*90c8c64dSAndroid Build Coastguard Worker import android.os.Build;
22*90c8c64dSAndroid Build Coastguard Worker import android.os.Environment;
23*90c8c64dSAndroid Build Coastguard Worker import android.util.Log;
24*90c8c64dSAndroid Build Coastguard Worker 
25*90c8c64dSAndroid Build Coastguard Worker import java.io.File;
26*90c8c64dSAndroid Build Coastguard Worker import java.text.SimpleDateFormat;
27*90c8c64dSAndroid Build Coastguard Worker import java.util.Date;
28*90c8c64dSAndroid Build Coastguard Worker import java.util.List;
29*90c8c64dSAndroid Build Coastguard Worker 
30*90c8c64dSAndroid Build Coastguard Worker /**
31*90c8c64dSAndroid Build Coastguard Worker  * Camera related utilities.
32*90c8c64dSAndroid Build Coastguard Worker  */
33*90c8c64dSAndroid Build Coastguard Worker public class CameraHelper {
34*90c8c64dSAndroid Build Coastguard Worker 
35*90c8c64dSAndroid Build Coastguard Worker     public static final int MEDIA_TYPE_IMAGE = 1;
36*90c8c64dSAndroid Build Coastguard Worker     public static final int MEDIA_TYPE_VIDEO = 2;
37*90c8c64dSAndroid Build Coastguard Worker 
38*90c8c64dSAndroid Build Coastguard Worker     /**
39*90c8c64dSAndroid Build Coastguard Worker      * Iterate over supported camera video sizes to see which one best fits the
40*90c8c64dSAndroid Build Coastguard Worker      * dimensions of the given view while maintaining the aspect ratio. If none can,
41*90c8c64dSAndroid Build Coastguard Worker      * be lenient with the aspect ratio.
42*90c8c64dSAndroid Build Coastguard Worker      *
43*90c8c64dSAndroid Build Coastguard Worker      * @param supportedVideoSizes Supported camera video sizes.
44*90c8c64dSAndroid Build Coastguard Worker      * @param previewSizes Supported camera preview sizes.
45*90c8c64dSAndroid Build Coastguard Worker      * @param w     The width of the view.
46*90c8c64dSAndroid Build Coastguard Worker      * @param h     The height of the view.
47*90c8c64dSAndroid Build Coastguard Worker      * @return Best match camera video size to fit in the view.
48*90c8c64dSAndroid Build Coastguard Worker      */
getOptimalVideoSize(List<Camera.Size> supportedVideoSizes, List<Camera.Size> previewSizes, int w, int h)49*90c8c64dSAndroid Build Coastguard Worker     public static Camera.Size getOptimalVideoSize(List<Camera.Size> supportedVideoSizes,
50*90c8c64dSAndroid Build Coastguard Worker             List<Camera.Size> previewSizes, int w, int h) {
51*90c8c64dSAndroid Build Coastguard Worker         // Use a very small tolerance because we want an exact match.
52*90c8c64dSAndroid Build Coastguard Worker         final double ASPECT_TOLERANCE = 0.1;
53*90c8c64dSAndroid Build Coastguard Worker         double targetRatio = (double) w / h;
54*90c8c64dSAndroid Build Coastguard Worker 
55*90c8c64dSAndroid Build Coastguard Worker         // Supported video sizes list might be null, it means that we are allowed to use the preview
56*90c8c64dSAndroid Build Coastguard Worker         // sizes
57*90c8c64dSAndroid Build Coastguard Worker         List<Camera.Size> videoSizes;
58*90c8c64dSAndroid Build Coastguard Worker         if (supportedVideoSizes != null) {
59*90c8c64dSAndroid Build Coastguard Worker             videoSizes = supportedVideoSizes;
60*90c8c64dSAndroid Build Coastguard Worker         } else {
61*90c8c64dSAndroid Build Coastguard Worker             videoSizes = previewSizes;
62*90c8c64dSAndroid Build Coastguard Worker         }
63*90c8c64dSAndroid Build Coastguard Worker         Camera.Size optimalSize = null;
64*90c8c64dSAndroid Build Coastguard Worker 
65*90c8c64dSAndroid Build Coastguard Worker         // Start with max value and refine as we iterate over available video sizes. This is the
66*90c8c64dSAndroid Build Coastguard Worker         // minimum difference between view and camera height.
67*90c8c64dSAndroid Build Coastguard Worker         double minDiff = Double.MAX_VALUE;
68*90c8c64dSAndroid Build Coastguard Worker 
69*90c8c64dSAndroid Build Coastguard Worker         // Target view height
70*90c8c64dSAndroid Build Coastguard Worker         int targetHeight = h;
71*90c8c64dSAndroid Build Coastguard Worker 
72*90c8c64dSAndroid Build Coastguard Worker         // Try to find a video size that matches aspect ratio and the target view size.
73*90c8c64dSAndroid Build Coastguard Worker         // Iterate over all available sizes and pick the largest size that can fit in the view and
74*90c8c64dSAndroid Build Coastguard Worker         // still maintain the aspect ratio.
75*90c8c64dSAndroid Build Coastguard Worker         for (Camera.Size size : videoSizes) {
76*90c8c64dSAndroid Build Coastguard Worker             double ratio = (double) size.width / size.height;
77*90c8c64dSAndroid Build Coastguard Worker             if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
78*90c8c64dSAndroid Build Coastguard Worker                 continue;
79*90c8c64dSAndroid Build Coastguard Worker             if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
80*90c8c64dSAndroid Build Coastguard Worker                 optimalSize = size;
81*90c8c64dSAndroid Build Coastguard Worker                 minDiff = Math.abs(size.height - targetHeight);
82*90c8c64dSAndroid Build Coastguard Worker             }
83*90c8c64dSAndroid Build Coastguard Worker         }
84*90c8c64dSAndroid Build Coastguard Worker 
85*90c8c64dSAndroid Build Coastguard Worker         // Cannot find video size that matches the aspect ratio, ignore the requirement
86*90c8c64dSAndroid Build Coastguard Worker         if (optimalSize == null) {
87*90c8c64dSAndroid Build Coastguard Worker             minDiff = Double.MAX_VALUE;
88*90c8c64dSAndroid Build Coastguard Worker             for (Camera.Size size : videoSizes) {
89*90c8c64dSAndroid Build Coastguard Worker                 if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
90*90c8c64dSAndroid Build Coastguard Worker                     optimalSize = size;
91*90c8c64dSAndroid Build Coastguard Worker                     minDiff = Math.abs(size.height - targetHeight);
92*90c8c64dSAndroid Build Coastguard Worker                 }
93*90c8c64dSAndroid Build Coastguard Worker             }
94*90c8c64dSAndroid Build Coastguard Worker         }
95*90c8c64dSAndroid Build Coastguard Worker         return optimalSize;
96*90c8c64dSAndroid Build Coastguard Worker     }
97*90c8c64dSAndroid Build Coastguard Worker 
98*90c8c64dSAndroid Build Coastguard Worker     /**
99*90c8c64dSAndroid Build Coastguard Worker      * @return the default camera on the device. Return null if there is no camera on the device.
100*90c8c64dSAndroid Build Coastguard Worker      */
getDefaultCameraInstance()101*90c8c64dSAndroid Build Coastguard Worker     public static Camera getDefaultCameraInstance() {
102*90c8c64dSAndroid Build Coastguard Worker         return Camera.open();
103*90c8c64dSAndroid Build Coastguard Worker     }
104*90c8c64dSAndroid Build Coastguard Worker 
105*90c8c64dSAndroid Build Coastguard Worker 
106*90c8c64dSAndroid Build Coastguard Worker     /**
107*90c8c64dSAndroid Build Coastguard Worker      * @return the default rear/back facing camera on the device. Returns null if camera is not
108*90c8c64dSAndroid Build Coastguard Worker      * available.
109*90c8c64dSAndroid Build Coastguard Worker      */
getDefaultBackFacingCameraInstance()110*90c8c64dSAndroid Build Coastguard Worker     public static Camera getDefaultBackFacingCameraInstance() {
111*90c8c64dSAndroid Build Coastguard Worker         return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
112*90c8c64dSAndroid Build Coastguard Worker     }
113*90c8c64dSAndroid Build Coastguard Worker 
114*90c8c64dSAndroid Build Coastguard Worker     /**
115*90c8c64dSAndroid Build Coastguard Worker      * @return the default front facing camera on the device. Returns null if camera is not
116*90c8c64dSAndroid Build Coastguard Worker      * available.
117*90c8c64dSAndroid Build Coastguard Worker      */
getDefaultFrontFacingCameraInstance()118*90c8c64dSAndroid Build Coastguard Worker     public static Camera getDefaultFrontFacingCameraInstance() {
119*90c8c64dSAndroid Build Coastguard Worker         return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_FRONT);
120*90c8c64dSAndroid Build Coastguard Worker     }
121*90c8c64dSAndroid Build Coastguard Worker 
122*90c8c64dSAndroid Build Coastguard Worker 
123*90c8c64dSAndroid Build Coastguard Worker     /**
124*90c8c64dSAndroid Build Coastguard Worker      *
125*90c8c64dSAndroid Build Coastguard Worker      * @param position Physical position of the camera i.e Camera.CameraInfo.CAMERA_FACING_FRONT
126*90c8c64dSAndroid Build Coastguard Worker      *                 or Camera.CameraInfo.CAMERA_FACING_BACK.
127*90c8c64dSAndroid Build Coastguard Worker      * @return the default camera on the device. Returns null if camera is not available.
128*90c8c64dSAndroid Build Coastguard Worker      */
129*90c8c64dSAndroid Build Coastguard Worker     @TargetApi(Build.VERSION_CODES.GINGERBREAD)
getDefaultCamera(int position)130*90c8c64dSAndroid Build Coastguard Worker     private static Camera getDefaultCamera(int position) {
131*90c8c64dSAndroid Build Coastguard Worker         // Find the total number of cameras available
132*90c8c64dSAndroid Build Coastguard Worker         int  mNumberOfCameras = Camera.getNumberOfCameras();
133*90c8c64dSAndroid Build Coastguard Worker 
134*90c8c64dSAndroid Build Coastguard Worker         // Find the ID of the back-facing ("default") camera
135*90c8c64dSAndroid Build Coastguard Worker         Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
136*90c8c64dSAndroid Build Coastguard Worker         for (int i = 0; i < mNumberOfCameras; i++) {
137*90c8c64dSAndroid Build Coastguard Worker             Camera.getCameraInfo(i, cameraInfo);
138*90c8c64dSAndroid Build Coastguard Worker             if (cameraInfo.facing == position) {
139*90c8c64dSAndroid Build Coastguard Worker                 return Camera.open(i);
140*90c8c64dSAndroid Build Coastguard Worker 
141*90c8c64dSAndroid Build Coastguard Worker             }
142*90c8c64dSAndroid Build Coastguard Worker         }
143*90c8c64dSAndroid Build Coastguard Worker 
144*90c8c64dSAndroid Build Coastguard Worker         return null;
145*90c8c64dSAndroid Build Coastguard Worker     }
146*90c8c64dSAndroid Build Coastguard Worker 
147*90c8c64dSAndroid Build Coastguard Worker     /**
148*90c8c64dSAndroid Build Coastguard Worker      * Creates a media file in the {@code Environment.DIRECTORY_PICTURES} directory. The directory
149*90c8c64dSAndroid Build Coastguard Worker      * is persistent and available to other applications like gallery.
150*90c8c64dSAndroid Build Coastguard Worker      *
151*90c8c64dSAndroid Build Coastguard Worker      * @param type Media type. Can be video or image.
152*90c8c64dSAndroid Build Coastguard Worker      * @return A file object pointing to the newly created file.
153*90c8c64dSAndroid Build Coastguard Worker      */
getOutputMediaFile(int type)154*90c8c64dSAndroid Build Coastguard Worker     public  static File getOutputMediaFile(int type){
155*90c8c64dSAndroid Build Coastguard Worker         // To be safe, you should check that the SDCard is mounted
156*90c8c64dSAndroid Build Coastguard Worker         // using Environment.getExternalStorageState() before doing this.
157*90c8c64dSAndroid Build Coastguard Worker         if (!Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {
158*90c8c64dSAndroid Build Coastguard Worker             return  null;
159*90c8c64dSAndroid Build Coastguard Worker         }
160*90c8c64dSAndroid Build Coastguard Worker 
161*90c8c64dSAndroid Build Coastguard Worker         File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
162*90c8c64dSAndroid Build Coastguard Worker                 Environment.DIRECTORY_PICTURES), "CameraSample");
163*90c8c64dSAndroid Build Coastguard Worker         // This location works best if you want the created images to be shared
164*90c8c64dSAndroid Build Coastguard Worker         // between applications and persist after your app has been uninstalled.
165*90c8c64dSAndroid Build Coastguard Worker 
166*90c8c64dSAndroid Build Coastguard Worker         // Create the storage directory if it does not exist
167*90c8c64dSAndroid Build Coastguard Worker         if (! mediaStorageDir.exists()){
168*90c8c64dSAndroid Build Coastguard Worker             if (! mediaStorageDir.mkdirs()) {
169*90c8c64dSAndroid Build Coastguard Worker                 Log.d("CameraSample", "failed to create directory");
170*90c8c64dSAndroid Build Coastguard Worker                 return null;
171*90c8c64dSAndroid Build Coastguard Worker             }
172*90c8c64dSAndroid Build Coastguard Worker         }
173*90c8c64dSAndroid Build Coastguard Worker 
174*90c8c64dSAndroid Build Coastguard Worker         // Create a media file name
175*90c8c64dSAndroid Build Coastguard Worker         String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
176*90c8c64dSAndroid Build Coastguard Worker         File mediaFile;
177*90c8c64dSAndroid Build Coastguard Worker         if (type == MEDIA_TYPE_IMAGE){
178*90c8c64dSAndroid Build Coastguard Worker             mediaFile = new File(mediaStorageDir.getPath() + File.separator +
179*90c8c64dSAndroid Build Coastguard Worker                     "IMG_"+ timeStamp + ".jpg");
180*90c8c64dSAndroid Build Coastguard Worker         } else if(type == MEDIA_TYPE_VIDEO) {
181*90c8c64dSAndroid Build Coastguard Worker             mediaFile = new File(mediaStorageDir.getPath() + File.separator +
182*90c8c64dSAndroid Build Coastguard Worker                     "VID_"+ timeStamp + ".mp4");
183*90c8c64dSAndroid Build Coastguard Worker         } else {
184*90c8c64dSAndroid Build Coastguard Worker             return null;
185*90c8c64dSAndroid Build Coastguard Worker         }
186*90c8c64dSAndroid Build Coastguard Worker 
187*90c8c64dSAndroid Build Coastguard Worker         return mediaFile;
188*90c8c64dSAndroid Build Coastguard Worker     }
189*90c8c64dSAndroid Build Coastguard Worker 
190*90c8c64dSAndroid Build Coastguard Worker }
191